changeset 5817:a1dd784d8b07

merged.
author Raimund Renkert <rrenkert@intevation.de>
date Wed, 24 Apr 2013 15:20:50 +0200
parents 76d40d257c8c (current diff) e0ab5a566688 (diff)
children a4ff4167be1e
files
diffstat 46 files changed, 761 insertions(+), 212 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/doc/conf/artifacts/chart.xml	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/doc/conf/artifacts/chart.xml	Wed Apr 24 15:20:50 2013 +0200
@@ -74,6 +74,8 @@
                         <facet name="discharge_longitudinal_section.q"/>
                         <facet name="discharge_longitudinal_section.c"/>
                         <facet name="other.wqkms"/>
+                        <facet name="other.wqkms.q"/>
+                        <facet name="other.wqkms.w"/>
                         <facet name="other.wkms"/>
                         <facet name="heightmarks_points"/>
                         <facet name="longitudinal_section.area"        description="an area"/>
--- a/flys-artifacts/doc/conf/artifacts/staticwqkms.xml	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/doc/conf/artifacts/staticwqkms.xml	Wed Apr 24 15:20:50 2013 +0200
@@ -9,6 +9,8 @@
           <facets>
             <facet name="other.wqkms.w" description="W-type data" />
             <facet name="other.wqkms.q" description="Q-type data" />
+            <facet name="relativepoint" description="Point data" />
+            <facet name="cross_section_water_line" description="Waterline for cross sections" />
           </facets>
         </outputmode>
       </outputmodes>
--- a/flys-artifacts/doc/conf/meta-data.xml	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/doc/conf/meta-data.xml	Wed Apr 24 15:20:50 2013 +0200
@@ -84,7 +84,7 @@
                       <dc:for-each>
                         <column name="{$prot_column_name}"
                                 ids="base_data-wstv-{$prot_rel_pos}-{$prot_id}"
-                                factory="staticwkms"
+                                factory="staticwqkms"
                                 info="{$info} [km {$deffrom} - {$defto}]"/>
                       </dc:for-each>
                     </dc:context>
@@ -1581,24 +1581,23 @@
             <dc:context>
               <dc:statement>
                 SELECT DISTINCT
-                b.kind_id AS building_kind_id,
                 b.name AS building_name,
-                bk.name AS building_kind
+                bk.name AS building_kind,
+                b.kind_id AS building_kind_id
                 FROM buildings b
                 JOIN building_kinds bk ON b.kind_id = bk.id
                 WHERE river_id = ${river_id}
-                AND kind_id = 0 OR
-                kind_id IS NULL
+                AND kind_id = 0
               </dc:statement>
-              <dc:for-each>
-                <dc:element name="${building_kind}">
+              <dc:if test="dc:has-result()">
+                <other>
                   <dc:for-each>
                     <buildings description="{$building_name}"
-                               factory="wmsbuildingsfactory"
-                               ids="{$river_id};{$building_name}"/>
+                      factory="wmsbuildingsfactory"
+                      ids="{$river_id};{$building_name}"/>
                   </dc:for-each>
-                </dc:element>
-              </dc:for-each>
+                </other>
+              </dc:if>
             </dc:context>
             <dc:context>
               <dc:statement>
--- a/flys-artifacts/doc/conf/themes.xml	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/doc/conf/themes.xml	Wed Apr 24 15:20:50 2013 +0200
@@ -192,7 +192,7 @@
         <mapping from="discharge_longitudinal_section.w" to="DischargeLongitudinalSectionW" />
         <mapping from="discharge_longitudinal_section.c" to="DischargeLongitudinalSectionC" />
         <mapping from="discharge_longitudinal_section.q" to="DischargeLongitudinalSectionQ" />
-        <mapping from="computed_discharge_curve.mainvalues.q" to="MainValuesQ" />
+        <mapping from="computed_discharge_curve.mainvalues.q" to="MainValuesQVerticalText" />
         <mapping from="computed_discharge_curve.mainvalues.w" to="MainValuesW" />
         <mapping from="duration_curve.mainvalues.q" to="MainValuesQ" />
         <mapping from="mainvalues.q" to="MainValuesQ" />
--- a/flys-artifacts/doc/conf/themes/default/general.xml	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/doc/conf/themes/default/general.xml	Wed Apr 24 15:20:50 2013 +0200
@@ -12,6 +12,20 @@
             <field name="showlinelabel" type="boolean"
                 display="Linienbeschriftung anzeigen" default="false" hints="hidden" />
         </fields>
+</theme>
+
+    <theme name="MainValuesQVerticalText">
+        <inherits>
+            <inherit from="Lines" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe"
+                default="200, 0, 15" />
+            <field name="textorientation" type="boolean" display="Textausrichtung"
+                default="false" />
+            <field name="showlinelabel" type="boolean"
+                display="Linienbeschriftung anzeigen" default="false" hints="hidden" />
+        </fields>
     </theme>
 
     <theme name="MainValuesW">
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWQKmsArtifact.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWQKmsArtifact.java	Wed Apr 24 15:20:50 2013 +0200
@@ -1,5 +1,7 @@
 package de.intevation.flys.artifacts;
 
+import java.awt.geom.Point2D;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -12,13 +14,19 @@
 
 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;
@@ -33,7 +41,7 @@
  */
 public class StaticWQKmsArtifact
 extends      StaticFLYSArtifact
-implements   FacetTypes
+implements   FacetTypes, WaterLineArtifact
 {
     /** The logger for this class. */
     private static Logger logger =
@@ -107,9 +115,9 @@
         CallMeta meta)
     {
         logger.debug("StaticWQKmsArtifact.initialize");
-        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        FLYSArtifact flys = (FLYSArtifact) artifact;
         // TODO: The river is of no interest, so far., also use importData
-        importData(winfo, "river");
+        importData(flys, "river");
 
         List<Facet> fs = new ArrayList<Facet>();
 
@@ -150,6 +158,52 @@
         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	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java	Wed Apr 24 15:20:50 2013 +0200
@@ -252,7 +252,7 @@
             Gauge gauge = river.determineGaugeByPosition(range[0]);
             if (gauge == null) {
                 return error(
-                    new WQKms[0], "no.gauge.found.for.km");
+                    new WQKms[0], "no.gauge.found.for.km", range[0]);
             }
 
             refKm = gauge.getStation().doubleValue();
@@ -416,6 +416,11 @@
         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.
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/BuilderPool.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/BuilderPool.java	Wed Apr 24 15:20:50 2013 +0200
@@ -7,13 +7,9 @@
 
 import java.sql.SQLException;
 
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-
-import javax.xml.transform.dom.DOMResult;
-import javax.xml.transform.dom.DOMSource;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.log4j.Logger;
 
@@ -46,32 +42,29 @@
 
         pool = new ArrayDeque<Builder>(poolSize);
         for (int i = 0; i < poolSize; ++i) {
-            Document doc = i > 0 // Clone all but the first.
-                ? cloneDocument(document)
-                : document;
-            pool.add(new Builder(doc));
+            pool.add(new Builder(cloneDocument(document)));
         }
     }
 
     private final static Document cloneDocument(Document document) {
+
         try {
-            TransformerFactory tfactory = TransformerFactory.newInstance();
-            Transformer xformer = tfactory.newTransformer();
-            DOMSource src = new DOMSource(document);
-            DOMResult dst = new DOMResult();
-            xformer.transform(src, dst);
-            return (Document)dst.getNode();
+            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 (TransformerConfigurationException tce) {
-            log.error(tce);
-        }
-        catch (TransformerException te) {
-            log.error(te);
+        catch (ParserConfigurationException pce) {
+            log.error(pce);
         }
 
-        log.error(
-            "Returning original DOM document. " +
-            "This will result in threading errors!");
+        log.error("Returning original document. This will lead to threading issues.");
 
         return document;
     }
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java	Wed Apr 24 15:20:50 2013 +0200
@@ -7,6 +7,9 @@
 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;
@@ -297,6 +300,16 @@
     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.
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation.java	Wed Apr 24 15:20:50 2013 +0200
@@ -92,6 +92,11 @@
         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>();
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsOverview.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsOverview.java	Wed Apr 24 15:20:50 2013 +0200
@@ -194,6 +194,15 @@
                 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 =
@@ -340,7 +349,7 @@
             Filter       filter
         ) {
             for (Column column: columns) {
-                if ((range == null || column.intersects(range))
+                if ((range == null || column.hasSectorsInRange(range))
                 && (filter == null || filter.accept(column))) {
                     allColumns.add(column);
                 }
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RelativePointFacet.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RelativePointFacet.java	Wed Apr 24 15:20:50 2013 +0200
@@ -10,6 +10,7 @@
 import de.intevation.artifacts.DataProvider;
 
 import de.intevation.flys.artifacts.StaticWKmsArtifact;
+import de.intevation.flys.artifacts.StaticWQKmsArtifact;
 import de.intevation.flys.artifacts.math.Linear;
 
 /**
@@ -36,7 +37,7 @@
 
 
     protected Point2D calculateDurationCurvePoint(CallContext context,
-        StaticWKmsArtifact artifact)
+        WKms wKms)
     {
         // TODO here and in reference curve calc: Do warn if more than 1
         // provider found or (way better) handle it.
@@ -69,7 +70,7 @@
 
         if (wqdays != null) {
             // Which W at this km?
-            double w = artifact.getWAtKmLin(artifact.getWKms(0), km);
+            double w = StaticWKmsArtifact.getWAtKmLin(wKms, km);
             if (w == -1) {
                 logger.warn("w is -1, already bad sign!");
             }
@@ -110,7 +111,8 @@
      * the Ws from start and end km param of the reference curve.
      */
     public Point2D calculateReferenceCurvePoint(CallContext context,
-        StaticWKmsArtifact artifact) {
+        WKms wKms) {
+
         List<DataProvider> providers = context.
             getDataProvider(ReferenceCurveFacet.BB_REFERENCECURVE_STARTKM);
         if (providers.size() < 1) {
@@ -129,9 +131,10 @@
             provideData(ReferenceCurveFacet.BB_REFERENCECURVE_ENDKMS, null, context);
 
         logger.debug("Got s " + start + " e " + ends);
-        double startW = artifact.getWAtKmLin(artifact.getWKms(0), start.doubleValue());
+
+        double startW = StaticWKmsArtifact.getWAtKmLin(wKms, start.doubleValue());
         // TODO handle multiple ends.
-        double endW = artifact.getWAtKmLin(artifact.getWKms(0), ends[0]);
+        double endW = StaticWKmsArtifact.getWAtKmLin(wKms, ends[0]);
         logger.debug("Gotw s " + startW + " e " + endW);
         return new Point2D.Double(startW, endW);
     }
@@ -147,17 +150,28 @@
      */
     @Override
     public Object getData(Artifact artifact, CallContext context) {
-        StaticWKmsArtifact staticData = (StaticWKmsArtifact) artifact;
+        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, staticData);
+            return calculateDurationCurvePoint(context, wKms);
         }
         else if (context.getDataProvider(
             ReferenceCurveFacet.BB_REFERENCECURVE_STARTKM).size() > 0) {
-            return calculateReferenceCurvePoint(context, staticData);
+            return calculateReferenceCurvePoint(context, wKms);
         }
 
         // TODO better signal failure.
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoad.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoad.java	Wed Apr 24 15:20:50 2013 +0200
@@ -6,7 +6,7 @@
 
 import de.intevation.flys.artifacts.model.NamedObjectImpl;
 
-
+/** Gives access to Fractions (at kms). */
 public class SedimentLoad
 extends NamedObjectImpl
 {
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadCalculation.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadCalculation.java	Wed Apr 24 15:20:50 2013 +0200
@@ -233,6 +233,8 @@
         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,
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadFactory.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadFactory.java	Wed Apr 24 15:20:50 2013 +0200
@@ -338,6 +338,13 @@
     }
 
 
+    /**
+     * 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,
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadResult.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/minfo/SedimentLoadResult.java	Wed Apr 24 15:20:50 2013 +0200
@@ -7,6 +7,8 @@
 
 import org.apache.log4j.Logger;
 
+
+/** Result from a SedimentLoadCalculation. */
 public class SedimentLoadResult
 implements Serializable
 {
@@ -61,6 +63,8 @@
         };
     }
 
+    /** 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();
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticWQKmsState.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StaticWQKmsState.java	Wed Apr 24 15:20:50 2013 +0200
@@ -12,7 +12,9 @@
 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;
 
@@ -82,8 +84,15 @@
                 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
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/process/WOutProcessor.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/process/WOutProcessor.java	Wed Apr 24 15:20:50 2013 +0200
@@ -16,10 +16,13 @@
 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);
 
@@ -65,17 +68,17 @@
      * Returns true if facettype is longitutinal_section.w .
      */
     @Override
-    public boolean canHandle(String facettype) {
-        if (facettype == null) {
+    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))
+        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;
         }
--- a/flys-artifacts/src/main/resources/messages.properties	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/resources/messages.properties	Wed Apr 24 15:20:50 2013 +0200
@@ -459,7 +459,7 @@
 converting.ws.to.qs.failed = Converting Ws to Qs failed.
 no.wst.for.river = No WST found for selected river.
 no.range.found = No range found.
-no.gauge.found.for.km = No gauge found for KM.
+no.gauge.found.for.km = No gauge found for KM {0}.
 cannot.create.segments = Cannot create segments.
 cannot.compute.discharge.curve = Cannot create discharge curve.
 cannot.find.ds = Cannot find Ds.
--- a/flys-artifacts/src/main/resources/messages_de.properties	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/resources/messages_de.properties	Wed Apr 24 15:20:50 2013 +0200
@@ -460,7 +460,7 @@
 converting.ws.to.qs.failed = Konvertierung von Ws zu Qs fehlgeschlagen.
 no.wst.for.river = Keine zum Gew\u00e4sser passende WST gefunden.
 no.range.found = Kein passender Pegel gefunden.
-no.gauge.found.for.km = Zur gegebenen Kilometerstation existiert kein Pegel.
+no.gauge.found.for.km = Zur gegebenen Kilometerstation {0} existiert kein Pegel.
 cannot.create.segments = Flussabschnitte konnten nicht erzeugt werden.
 cannot.compute.discharge.curve = Die Abflusskurve konnte nicht berechnet werden..
 cannot.find.ds = Dauerzahlen konnten nicht gefunden werden.
--- a/flys-artifacts/src/main/resources/messages_de_DE.properties	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/resources/messages_de_DE.properties	Wed Apr 24 15:20:50 2013 +0200
@@ -456,7 +456,7 @@
 converting.ws.to.qs.failed = Konvertierung von Ws zu Qs fehlgeschlagen.
 no.wst.for.river = Keine zum Gew\u00e4sser passende WST gefunden.
 no.range.found = Kein passender Pegel gefunden.
-no.gauge.found.for.km = Zur gegebenen Kilometerstation existiert kein Pegel.
+no.gauge.found.for.km = Zur gegebenen Kilometerstation {0} existiert kein Pegel.
 cannot.create.segments = Flussabschnitte konnten nicht erzeugt werden.
 cannot.compute.discharge.curve = Die Abflusskurve konnte nicht berechnet werden..
 cannot.find.ds = Dauerzahlen konnten nicht gefunden werden.
--- a/flys-artifacts/src/main/resources/messages_en.properties	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-artifacts/src/main/resources/messages_en.properties	Wed Apr 24 15:20:50 2013 +0200
@@ -461,7 +461,7 @@
 converting.ws.to.qs.failed = Converting Ws to Qs failed.
 no.wst.for.river = No WST found for selected river.
 no.range.found = No range found.
-no.gauge.found.for.km = No gauge found for KM.
+no.gauge.found.for.km = No gauge found for KM {0}.
 cannot.create.segments = Cannot create segments.
 cannot.compute.discharge.curve = Cannot create discharge curve.
 cannot.find.ds = Cannot find Ds.
--- a/flys-backend/contrib/run_hydr_morph.sh	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/contrib/run_hydr_morph.sh	Wed Apr 24 15:20:50 2013 +0200
@@ -39,6 +39,7 @@
 IMPORTER_SKIP_OFFICIAL_LINES=false
 IMPORTER_SKIP_PRFS=false
 IMPORTER_SKIP_W80S=false
+IMPORTER_SKIP_W80_CSVS=false
 IMPORTER_SKIP_WST=false
 
 IMPORTER_SKIP_BED_HEIGHT_SINGLE=false
@@ -95,6 +96,7 @@
     -Dflys.backend.importer.skip.sediment.yield=$IMPORTER_SKIP_SEDIMENT_YIELD \
     -Dflys.backend.importer.skip.sq.relation=$IMPORTER_SKIP_SQ_RELATION \
     -Dflys.backend.importer.skip.w80s=$IMPORTER_SKIP_W80S \
+    -Dflys.backend.importer.skip.w80.csvs=$IMPORTER_SKIP_W80_CSVS \
     -Dflys.backend.importer.skip.waterlevels=$IMPORTER_SKIP_WATERLEVELS \
     -Dflys.backend.importer.skip.waterlevel.differences=$IMPORTER_SKIP_WATERLEVEL_DIFFERENCES \
     -Dflys.backend.importer.skip.wst=$IMPORTER_SKIP_WST \
--- a/flys-backend/doc/schema/oracle-spatial.sql	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/doc/schema/oracle-spatial.sql	Wed Apr 24 15:20:50 2013 +0200
@@ -34,7 +34,7 @@
     OGR_FID NUMBER(38),
     GEOM MDSYS.SDO_GEOMETRY,
     river_id NUMBER(38) REFERENCES rivers(id) ON DELETE CASCADE,
-    km NUMBER(7,3),
+    km NUMBER(7,3) NOT NULL,
     name     VARCHAR(64),
     path     VARCHAR(256),
     ID NUMBER PRIMARY KEY NOT NULL
@@ -313,7 +313,7 @@
     OGR_FID NUMBER(38),
     GEOM MDSYS.SDO_GEOMETRY,
     river_id NUMBER(38) REFERENCES rivers(id) ON DELETE CASCADE,
-    name VARCHAR(255),
+    name VARCHAR(255) NOT NULL,
     kind NUMBER NOT NULL REFERENCES floodmap_kinds(id),
     diff NUMBER(19,5),
     count NUMBER(38),
--- a/flys-backend/doc/schema/oracle.sql	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/doc/schema/oracle.sql	Wed Apr 24 15:20:50 2013 +0200
@@ -3,7 +3,7 @@
 
 CREATE TABLE annotation_types (
     id              NUMBER(38,0) NOT NULL, 
-    name            VARCHAR2(255),
+    name            VARCHAR2(255) NOT NULL UNIQUE,
     PRIMARY KEY     (id)
 );
 
@@ -12,8 +12,8 @@
 CREATE SEQUENCE ANNOTATIONS_ID_SEQ;
 
 CREATE TABLE annotations (
-    id              NUMBER(38,0) NOT NULL, 
-    attribute_id    NUMBER(38,0),
+    id              NUMBER(38,0) NOT NULL,
+    attribute_id    NUMBER(38,0) NOT NULL,
     edge_id         NUMBER(38,0),
     position_id     NUMBER(38,0),
     range_id        NUMBER(38,0),
@@ -27,7 +27,7 @@
 
 CREATE TABLE attributes (
     id              NUMBER(38,0) NOT NULL, 
-    value           VARCHAR2(255), 
+    value           VARCHAR2(255) NOT NULL UNIQUE, 
     primary key     (id)
 );
 
@@ -78,9 +78,10 @@
 
 CREATE TABLE discharge_table_values (
     id                  NUMBER(38,0) NOT NULL,
-    q                   NUMBER(38,2),
-    w                   NUMBER(38,2),
-    table_id            NUMBER(38,0),
+    q                   NUMBER(38,2) NOT NULL,
+    w                   NUMBER(38,2) NOT NULL,
+    table_id            NUMBER(38,0) NOT NULL,
+    UNIQUE (table_id, q, w),
     PRIMARY KEY         (id)
 );
 
@@ -90,10 +91,10 @@
 
 CREATE TABLE discharge_tables (
     id                  NUMBER(38,0) NOT NULL,
-    description         VARCHAR2(255),
+    description         VARCHAR2(255) NOT NULL,
     bfg_id              VARCHAR2(50),
-    kind                NUMBER(38,0),
-    gauge_id            NUMBER(38,0),
+    kind                NUMBER(38,0) NOT NULL DEFAULT 0,
+    gauge_id            NUMBER(38,0) NOT NULL,
     time_interval_id    NUMBER(38,0),
     PRIMARY KEY         (id)
 );
@@ -122,7 +123,7 @@
     official_number     NUMBER(38,0) UNIQUE,
     range_id            NUMBER(38,0) NOT NULL,
     -- remove river id here because range_id references river already
-    river_id            NUMBER(38,0),
+    river_id            NUMBER(38,0) NOT NULL,
     PRIMARY KEY         (id),
     UNIQUE (name, river_id),
     UNIQUE (river_id, station)
@@ -197,7 +198,7 @@
 
 CREATE TABLE main_value_types (
     id                  NUMBER(38,0) NOT NULL,
-    name                VARCHAR2(255),
+    name                VARCHAR2(255) NOT NULL UNIQUE,
     PRIMARY KEY         (id)
 );
 
@@ -207,10 +208,13 @@
 
 CREATE TABLE main_values (
     id                  NUMBER(38,0) NOT NULL,
-    value               NUMBER(38,2),
-    gauge_id            NUMBER(38,0),
-    named_value_id      NUMBER(38,0),
+    value               NUMBER(38,2) NOT NULL,
+    gauge_id            NUMBER(38,0) NOT NULL,
+    named_value_id      NUMBER(38,0) NOT NULL,
     time_interval_id    NUMBER(38,0),
+
+    -- TODO: better checks
+    UNIQUE (gauge_id, named_value_id, time_interval_id),
     PRIMARY KEY         (id)
 );
 
@@ -221,7 +225,7 @@
 CREATE TABLE named_main_values (
     id                  NUMBER(38,0) NOT NULL,
     name                VARCHAR2(256) NOT NULL UNIQUE,
-    type_id             NUMBER(38,0),
+    type_id             NUMBER(38,0) NOT NULL,
     PRIMARY KEY (id)
 );
 
@@ -231,7 +235,7 @@
 
 CREATE TABLE positions (
     id                  NUMBER(38,0) NOT NULL,
-    value               VARCHAR2(255 char),
+    value               VARCHAR2(255 char) NOT NULL UNIQUE,
     PRIMARY KEY         (id)
 );
 
@@ -244,7 +248,8 @@
     a                   NUMBER(38,10) NOT NULL,
     b                   NUMBER(38,10),
     river_id            NUMBER(38,0),
-    PRIMARY KEY         (id)
+    UNIQUE (river_id, a, b),
+    PRIMARY KEY (id)
 );
 
 
@@ -254,10 +259,10 @@
 
 CREATE TABLE rivers (
     id                  NUMBER(38,0) NOT NULL,
-    official_number     NUMBER(38,0),
-    km_up               NUMBER(38,0),
-    name                VARCHAR2(255),
-    wst_unit_id         NUMBER(38,0),
+    official_number     NUMBER(38,0) UNIQUE,
+    km_up               NUMBER(38,0) NOT NULL DEFAULT 0,
+    name                VARCHAR2(255) NOT NULL UNIQUE,
+    wst_unit_id         NUMBER(38,0) NOT NULL,
     PRIMARY KEY         (id)
 );
 
@@ -279,7 +284,7 @@
 
 CREATE TABLE units (
     id                  NUMBER(38,0) NOT NULL,
-    name                VARCHAR2(255),
+    name                VARCHAR2(255) NOT NULL UNIQUE,
     PRIMARY KEY         (id)
 );
 
@@ -289,8 +294,9 @@
 
 CREATE TABLE wst_column_q_ranges (
     id                  NUMBER(38,0) NOT NULL,
-    wst_column_id       NUMBER(38,0),
-    wst_q_range_id      NUMBER(38,0),
+    wst_column_id       NUMBER(38,0) NOT NULL,
+    wst_q_range_id      NUMBER(38,0) NOT NULL,
+    UNIQUE (wst_column_id, wst_q_range_id),
     PRIMARY KEY         (id)
 );
 
@@ -300,9 +306,11 @@
 
 CREATE TABLE wst_column_values (
     id                  NUMBER(38,0) NOT NULL,
-    position            NUMBER(38,5),
-    w                   NUMBER(38,5),
-    wst_column_id       NUMBER(38,0),
+    position            NUMBER(38,5) NOT NULL,
+    w                   NUMBER(38,5) NOT NULL,
+    wst_column_id       NUMBER(38,0) NOT NULL,
+    UNIQUE (position, wst_column_id),
+    UNIQUE (position, wst_column_id, w),
     PRIMARY KEY         (id)
 );
 
@@ -313,10 +321,12 @@
 CREATE TABLE wst_columns (
     id                  NUMBER(38,0) NOT NULL,
     description         VARCHAR2(255),
-    name                VARCHAR2(255),
-    position            NUMBER(38,0),
+    name                VARCHAR2(255) NOT NULL,
+    position            NUMBER(38,0) NOT NULL DEFAULT 0,
     time_interval_id    NUMBER(38,0),
-    wst_id              NUMBER(38,0),
+    wst_id              NUMBER(38,0) NOT NULL,
+    UNIQUE (wst_id, name),
+    UNIQUE (wst_id, position),
     PRIMARY KEY         (id)
 );
 
@@ -326,8 +336,8 @@
 
 CREATE TABLE wst_q_ranges (
     id                  NUMBER(38,0) NOT NULL,
-    q                   NUMBER(38,5),
-    range_id            NUMBER(38,0),
+    q                   NUMBER(38,5) NOT NULL,
+    range_id            NUMBER(38,0) NOT NULL,
     PRIMARY KEY         (id)
 );
 
@@ -352,9 +362,10 @@
 
 CREATE TABLE wsts (
     id                  NUMBER(38,0) NOT NULL,
-    description         VARCHAR2(255),
-    kind                NUMBER(38,0),
-    river_id            NUMBER(38,0),
+    description         VARCHAR2(255) NOT NULL,
+    kind                NUMBER(38,0) NOT NULL,
+    river_id            NUMBER(38,0) NOT NULL,
+    UNIQUE (river_id, description),
     PRIMARY KEY         (id)
 );
 
--- a/flys-backend/doc/schema/postgresql-spatial.sql	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/doc/schema/postgresql-spatial.sql	Wed Apr 24 15:20:50 2013 +0200
@@ -125,7 +125,7 @@
     range_id         INT REFERENCES ranges(id),
     time_interval_id INT REFERENCES time_intervals(id),
     projection       VARCHAR(32),
-    srid	    int  NOT NULL,
+    srid	     int NOT NULL,
     elevation_state  VARCHAR(32),
     format           VARCHAR(32),
     border_break     BOOLEAN NOT NULL DEFAULT FALSE,
--- a/flys-backend/doc/schema/postgresql.sql	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/doc/schema/postgresql.sql	Wed Apr 24 15:20:50 2013 +0200
@@ -69,8 +69,8 @@
 
 CREATE TABLE annotations (
     id           int PRIMARY KEY NOT NULL,
-    range_id     int             NOT NULL REFERENCES ranges(id) ON DELETE CASCADE,
-    attribute_id int             NOT NULL REFERENCES attributes(id),
+    range_id     int NOT NULL REFERENCES ranges(id) ON DELETE CASCADE,
+    attribute_id int NOT NULL REFERENCES attributes(id),
     position_id  int REFERENCES positions(id),
     edge_id      int REFERENCES edges(id),
     type_id      int REFERENCES annotation_types(id)
--- a/flys-backend/src/main/java/de/intevation/flys/importer/Config.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/src/main/java/de/intevation/flys/importer/Config.java	Wed Apr 24 15:20:50 2013 +0200
@@ -35,6 +35,9 @@
     public static final String SKIP_W80S =
         "flys.backend.importer.skip.w80s";
 
+    public static final String SKIP_W80_CSVS =
+        "flys.backend.importer.skip.w80.csvs";
+
     public static final String SKIP_HYKS =
         "flys.backend.importer.skip.hyks";
 
@@ -138,6 +141,10 @@
         return getFlag(SKIP_DA50S);
     }
 
+    public boolean skipW80CSVs() {
+        return getFlag(SKIP_W80_CSVS);
+    }
+
     public boolean skipW80s() {
         return getFlag(SKIP_W80S);
     }
--- a/flys-backend/src/main/java/de/intevation/flys/importer/ImportGauge.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/src/main/java/de/intevation/flys/importer/ImportGauge.java	Wed Apr 24 15:20:50 2013 +0200
@@ -22,6 +22,7 @@
 import de.intevation.flys.importer.parsers.AtFileParser;
 import de.intevation.flys.importer.parsers.StaFileParser;
 
+/** Gauge not in DB. */
 public class ImportGauge
 {
     private static Logger log = Logger.getLogger(ImportGauge.class);
--- a/flys-backend/src/main/java/de/intevation/flys/importer/ImportRiver.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/src/main/java/de/intevation/flys/importer/ImportRiver.java	Wed Apr 24 15:20:50 2013 +0200
@@ -22,6 +22,7 @@
 import de.intevation.flys.importer.parsers.SedimentDensityParser;
 import de.intevation.flys.importer.parsers.SedimentYieldParser;
 import de.intevation.flys.importer.parsers.W80Parser;
+import de.intevation.flys.importer.parsers.W80CSVParser;
 import de.intevation.flys.importer.parsers.WaterlevelDifferencesParser;
 import de.intevation.flys.importer.parsers.WaterlevelParser;
 import de.intevation.flys.importer.parsers.WstParser;
@@ -167,7 +168,9 @@
     protected River peer;
 
 
-    /** Callback-implementation for CrossSectionParsers. */
+    /** Callback-implementation for CrossSectionParsers:
+     * Accept files with different md5(?)sums than what has already been parsed,
+     * on successfull parse, add data. */
     class ImportRiverCrossSectionParserCallback implements CrossSectionParser.Callback {
         Set<HashedFile> files = new HashSet<HashedFile>();
         String type;
@@ -194,7 +197,7 @@
 
 
         /** Add crosssection. */
-        public void    parsed(CrossSectionParser parser) {
+        public void parsed(CrossSectionParser parser) {
            log.debug("callback from " + type + " parser");
 
            addCrossSections(parser);
@@ -286,6 +289,7 @@
         parseDA66s();
         parseDA50s();
         parseW80s();
+        parseW80CSVs();
         parseHYKs();
         parseWst();
         parseExtraWsts();
@@ -1003,6 +1007,29 @@
         parser.parseW80s(riverDir, w80Callback);
     }
 
+    /** Create a W80 Parser and parse w80 files found. */
+    public void parseW80CSVs() {
+        if (Config.INSTANCE.skipW80CSVs()) {
+            log.info("skip parsing W80 csvs");
+            return;
+        }
+        W80CSVParser parser = new W80CSVParser();
+        File riverDir = wstFile
+            .getParentFile()  // Basisdaten
+            .getParentFile()  // Hydrologie
+            .getParentFile(); // <river>
+
+        // Construct the Cross-Section-Data path.
+        File csDir = new File(riverDir.getPath()
+            + File.separator + "Geodaesie"
+            + File.separator + "Querprofile"
+            + File.separator + "QP-Daten");
+
+        ImportRiverCrossSectionParserCallback w80CSVCallback =
+            new ImportRiverCrossSectionParserCallback("w80-csv");
+        parser.parseW80CSVs(csDir, w80CSVCallback);
+    }
+
 
     /**
      * Create and use a DA50Parser, parse the files found, add the
@@ -1116,7 +1143,11 @@
     }
 
     public void storeCrossSections() {
-        if (!Config.INSTANCE.skipPRFs() || !Config.INSTANCE.skipDA66s() || !Config.INSTANCE.skipDA50s() || !Config.INSTANCE.skipW80s()) {
+        if (!Config.INSTANCE.skipPRFs()
+            || !Config.INSTANCE.skipDA66s()
+            || !Config.INSTANCE.skipDA50s()
+            || !Config.INSTANCE.skipW80s()
+            || !Config.INSTANCE.skipW80CSVs()) {
             log.info("store cross sections");
             getPeer();
             for (ImportCrossSection crossSection: crossSections) {
--- a/flys-backend/src/main/java/de/intevation/flys/importer/ImportTimeInterval.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/src/main/java/de/intevation/flys/importer/ImportTimeInterval.java	Wed Apr 24 15:20:50 2013 +0200
@@ -24,6 +24,7 @@
 
     public ImportTimeInterval(Date startTime) {
         this.startTime = startTime;
+        this.stopTime = null;
     }
 
     public ImportTimeInterval(Date startTime, Date stopTime) {
@@ -67,10 +68,21 @@
     public TimeInterval getPeer() {
         if (peer == null) {
             Session session = ImporterSession.getInstance().getDatabaseSession();
-            Query query = session.createQuery(
-                "from TimeInterval where startTime=:a and stopTime=:b");
+            if (startTime == null) {
+                log.error("Null Start time will be ignored.");
+            }
+            Query query;
+            if (stopTime == null) {
+                query = session.createQuery(
+                    "from TimeInterval where startTime=:a and stopTime is null");
+            }
+            else {
+                query = session.createQuery(
+                    "from TimeInterval where startTime=:a and stopTime=:b");
+                query.setParameter("b", stopTime);
+            }
             query.setParameter("a", startTime);
-            query.setParameter("b",  stopTime);
+
             List<TimeInterval> intervals = query.list();
             if (intervals.isEmpty()) {
                 peer = new TimeInterval(startTime, stopTime);
--- a/flys-backend/src/main/java/de/intevation/flys/importer/parsers/LineParser.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/src/main/java/de/intevation/flys/importer/parsers/LineParser.java	Wed Apr 24 15:20:50 2013 +0200
@@ -45,6 +45,8 @@
     /** Name of file parsed. */
     protected String fileName;
 
+    protected File inputFile;
+
 
     /**
      * This method reads each line of <i>file</i>. At the beginning,
@@ -56,6 +58,8 @@
     public void parse(File file) throws IOException {
         log.info("Parsing file '" + file + "'");
 
+        inputFile = file;
+
         fileName = file.getName();
 
         reset();
@@ -93,6 +97,11 @@
         return fileName;
     }
 
+    /** Returns the file currently parsed. */
+    protected File getInputFile() {
+        return inputFile;
+    }
+
 
     protected static String stripMetaLine(String line) {
         String tmp = line.substring(1, line.length());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-backend/src/main/java/de/intevation/flys/importer/parsers/W80CSVParser.java	Wed Apr 24 15:20:50 2013 +0200
@@ -0,0 +1,291 @@
+package de.intevation.flys.importer.parsers;
+
+import de.intevation.artifacts.common.utils.FileTools;
+
+import de.intevation.flys.importer.XY;
+
+import de.intevation.flys.importer.parsers.tim.Coordinate;
+
+import de.intevation.flys.utils.DateGuesser;
+import de.intevation.flys.utils.EpsilonComparator;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * To create cross-sections, generate: Map<double,list<xy>> from files
+ * in w80/csv format.
+ */
+public class W80CSVParser extends LineParser implements CrossSectionParser
+{
+    /** Private logger. */
+    private static Logger logger = Logger.getLogger(W80CSVParser.class);
+
+
+    /** The current line to which add points. */
+    private List<XY> currentLine;
+
+
+    /** Data collected so far, last element will be currentLine. */
+    protected Map<Double, List<XY>> data;
+
+
+    /** Anchor to project to. */
+    private static class Anchor extends Coordinate {
+
+        private static final double EPSILON = 1e-5;
+
+        private double station;
+
+        public Anchor(double x, double y, double z, double station) {
+            super(x, y, z);
+            this.station = station;
+        }
+
+        public boolean sameStation(double station) {
+            return Math.abs(this.station - station) < EPSILON;
+        }
+    }
+
+
+    /** Reference point for simple projection. */
+    private Anchor anchor;
+
+
+    /**
+     * Reference point for distance calculations, introduced to
+     * deal with bends in the lines.
+     * Array has two entrys: first is GK-Right, second GK-High.
+     */
+    private double[] lastPointGK;
+
+
+    /** Measurement date of anchor as listed in w80 file. */
+    private Date anchorDate;
+
+
+    private double distanceToLastPoint(double gkr, double gkh) {
+        double dx = gkr - lastPointGK[0];
+        double dy = gkh - lastPointGK[1];
+        double d  = dx*dx + dy*dy;
+
+        return Math.sqrt(d);
+    }
+
+
+    /** Trivial constructor. */
+    public W80CSVParser() {
+        data = new TreeMap<Double, List<XY>>(EpsilonComparator.CMP);
+    }
+
+
+    /**
+     * Get the description of the cross section parsed -
+     * directory name of current file.
+     */
+    @Override
+    public String getDescription() {
+        return getInputFile().getParentFile().getName();
+    }
+
+
+    /** Get the year of this cross sections measurement. */
+    @Override
+    public Integer getYear() {
+        if (anchorDate == null) {
+            return null;
+        }
+        Calendar dateCalendar = Calendar.getInstance();
+        dateCalendar.setTime(anchorDate);
+        return dateCalendar.get(Calendar.YEAR);
+    }
+
+
+    /**
+     * Return the data parsed.
+     * @return map of stations (km) to list of points.
+     */
+    @Override
+    public Map<Double, List<XY>> getData() {
+        return data;
+    }
+
+
+    /** Recursively descend root, ask the callback for every file
+     * found and parse it if callback acks. When done, notify callback. */
+    public void parseW80CSVs(File root, final Callback callback) {
+
+        FileTools.walkTree(root, new FileTools.FileVisitor() {
+            @Override
+            public boolean visit(File file) {
+                if (file.isFile() && file.canRead()
+                && file.getName().toLowerCase().endsWith(".csv")
+                && (callback == null || callback.accept(file))) {
+                    reset();
+                    try {
+                        parse(file);
+                        logger.info("parsing done");
+                        if (callback != null) {
+                            callback.parsed(W80CSVParser.this);
+                        }
+                    }
+                    catch (IOException ioe) {
+                        logger.error("IOException while parsing file");
+                        return false;
+                    }
+                }
+                return true;
+            }
+        });
+    }
+
+
+    /** Called before consuming first line of file. */
+    public void reset() {
+        data.clear();
+        currentLine = new ArrayList<XY>();
+        anchor = null;
+        anchorDate = null;
+        lastPointGK = new double[] {0d,0d};
+    }
+
+
+    /**
+     * Get the Index of the last cross-section lines point.
+     * @return last points index, -1 if not available.
+     */
+    private int getLastPointIdx() {
+        if (currentLine == null || currentLine.isEmpty()) {
+            return -1;
+        }
+        XY lastPoint = this.currentLine.get(currentLine.size()-1);
+        return lastPoint.getIndex();
+    }
+
+
+    private double getLastPointX() {
+        if (currentLine == null || currentLine.isEmpty()) {
+            return 0d;
+        }
+        XY lastPoint = this.currentLine.get(currentLine.size()-1);
+        return lastPoint.getX();
+    }
+
+
+    /**
+     * Add a Point (YZ,Index) to the current cross section line.
+     * @param y The y coordinate of new point.
+     * @param z The z coordinate of new point.
+     * @param idx Ignored, the parameter of new point.
+     * @return true if point could been added, false otherwise (e.g. not
+     *         parsable y or z values.
+     */
+    private boolean addPoint(double gkr, double gkh, double height, String idx) {
+        // Calculate distance between this and lst point (add distances).
+        double d = distanceToLastPoint(gkr, gkh);
+        double totalX = getLastPointX() + d;
+
+        // We ignore idx, and increment instead.
+        int index;
+        int lastPointIdx = getLastPointIdx();
+        if (lastPointIdx <= 0) {
+            index = 1;
+        } else {
+            index = lastPointIdx + 1;
+        }
+
+        this.lastPointGK[0] = gkr;
+        this.lastPointGK[1] = gkh;
+        currentLine.add(new XY(totalX, height/1000d, index));
+        return true;
+    }
+
+    // As per documentation:
+    // BW;WPA;ST;UF;PN;LS;BL-LS;Y;X;Z;DL;LZK;SY;SX;SZ;BML;HS;BL-HS;H;DH;HZK;SH;WVA;BMH;BMP;DST;DB;LDS;LKZ;
+
+
+    /**
+     * Called for each line. Try to extract info from a w80 line.
+     * @param lineNum Number of line (starting with 1).
+     */
+    @Override
+    protected void handleLine(int lineNum, String line) {
+        // First two lines are 'comment'-like.
+        if (lineNum == 1 || lineNum == 2) {
+            return;
+        }
+        // The 'shore' field shows which side of the river the shore is measured.
+        // Therefore, the points have to be added in the correct order (also
+        // because later distances are calculated which cannot be
+        // negative.
+        String[] fields = line.split(";");
+        String station = fields[2];
+        String shore   = fields[3];
+        // TODO: There is 'station' and a 'shore'-code behind.
+        // 1 = left, 2 = right. none = middle
+        String pointIndex = line.substring(16,21);
+        // For GK, first seven digits are of interest.
+        String gkRight = fields[7];
+        String gkHigh  = fields[8];
+        String date    = fields[10];
+        String height  = fields[18];
+        String dateH   = line.substring(54,60);
+        String dateDec = line.substring(64,70);
+
+        double stationKm = Double.parseDouble(station) / 1000d;
+        double gkRightKm = Double.parseDouble(gkRight.replace(",","."));//.substring(0,7));
+        double gkHighKm  = Double.parseDouble(gkHigh.replace(",","."));//.substring(0,7));
+        double heightM   = Double.parseDouble(height.replace(",","."));
+
+        // New (or first) line.
+        if (anchor == null || !anchor.sameStation(stationKm)) {
+            anchor = new Anchor(gkRightKm, gkHighKm, heightM, stationKm);
+            lastPointGK[0] = gkRightKm;
+            lastPointGK[1] = gkHighKm;
+            currentLine = new ArrayList<XY>();
+            data.put(stationKm, currentLine);
+            currentLine.add(new XY(0d, heightM, 0));
+            try {
+                anchorDate = DateGuesser.guessDate(date);
+            }
+            catch (IllegalArgumentException iae) {
+                logger.warn("W80CSV: Invalid date '" + date + "'.");
+            }
+        }
+        else {
+            addPoint(gkRightKm, gkHighKm, heightM, pointIndex);
+        }
+    }
+
+
+    /** Called when file is fully consumed. */
+    @Override
+    protected void finish() {
+        logger.info("Parsed " + data.size() + " lines");
+    }
+
+
+    /** Parses files given as arguments. */
+    public static void main(String [] args) {
+
+        W80CSVParser parser = new W80CSVParser();
+
+        logger.warn("Start parsing files.");
+        for (String arg: args) {
+            logger.warn("Parsing a file.");
+            parser.parseW80CSVs(new File(arg), null);
+        }
+        logger.error("Finished parsing files.");
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-backend/src/main/java/de/intevation/flys/importer/parsers/WstParser.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-backend/src/main/java/de/intevation/flys/importer/parsers/WstParser.java	Wed Apr 24 15:20:50 2013 +0200
@@ -378,7 +378,7 @@
     ) {
         log.debug("addInterval: " + from + " " + to);
 
-        if (values == null || from == MAX_RANGE) {
+        if (values == null || from == MAX_RANGE || from == MIN_RANGE) {
             return;
         }
 
--- a/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants.java	Wed Apr 24 15:20:50 2013 +0200
@@ -1256,5 +1256,13 @@
 
     String mapLogo();
 
+    String wmsURLMenuItem();
+
+    String wmsURLBoxTitle();
+
+    String requireTheme();
+
+    String PATH();
+
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants.properties	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants.properties	Wed Apr 24 15:20:50 2013 +0200
@@ -220,8 +220,8 @@
 duration_curve = Duration Curve
 discharge_longitudinal_section = Discharge Longitudinal Section
 floodmap = Floodmap
-historical_discharge = Time Chart
-historical_discharge_wq = WQ Chart
+historical_discharge = Time-Chart
+historical_discharge_wq = W/Q-Chart
 flow_velocity = Flow Velocity
 flow_velocity_export = Flow Velocity Export
 bedheight_middle = Middle Bed Height
@@ -277,7 +277,7 @@
 gauge_class = Gauge Class
 eventselect = Eventselection
 events = Events
-kmchart = W/Q Preview
+kmchart = W/Q-Preview
 
 chart_themepanel_header_themes = Theme
 chart_themepanel_header_actions = Actions
@@ -389,7 +389,7 @@
 removeFeature = images/removeFeature.png
 getFeatureInfo = images/info.png
 getFeatureInfoTooltip = Information Tool
-getFeatureInfoWindowTitle = Informations for Map Layers.
+getFeatureInfoWindowTitle = Information for Map Layer:
 addWMS = images/add_map.png
 printMapSettings = images/print_map_settings.png
 addWMSTooltip = Load layers from external WMS service.
@@ -452,7 +452,7 @@
 land = Land
 rastermap = Rastermap
 background = Background Map
-discharge_tables_chart = WQ preview
+discharge_tables_chart = W/Q-Preview
 discharge_table_nn = Discharge Tables at Gauge
 discharge_table_gauge = Discharge Table at Gauge
 mainvalue = Mainvalue
@@ -591,6 +591,9 @@
 wsplgen_cat4 = Fill Color 3.0 <= DIFF < 4
 wsplgen_cat5 = Fill Color 4.0 <= DIFF
 attribution = &copyIntevation GmbH 2013<br>Data &copy<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>
+wmsURLMenuItem = WMS URL
+wmsURLBoxTitle = Layer WMS URL
+requireTheme = You need to choose a Layer.
 
 # Manual Points Editor
 addpoints = Add points
@@ -627,7 +630,7 @@
 gauge_river_url = https://flys-intern.intevation.de/GewaesserInfo/
 gauge_curve_link = Dischargecurve/-table
 discharge_timeranges = DC-Timeranges
-discharge_chart = WQ-Preview
+discharge_chart = W/Q-Preview
 
 measurement_station_type = Type of Measurement Station
 measurement_station_operator = Operator
@@ -661,3 +664,5 @@
 mapDate = Place, Date
 mapLogo = Logo
 
+# Get Feature Info Window
+PATH = Path
--- a/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties	Wed Apr 24 15:20:50 2013 +0200
@@ -220,8 +220,8 @@
 duration_curve = Dauerlinie
 discharge_longitudinal_section = W f\u00fcr benutzerdefinierten Abflussl\u00e4ngsschnitt
 floodmap = \u00dcberschwemmungsfl\u00e4che
-historical_discharge = Zeit Diagramm
-historical_discharge_wq = WQ Diagramm
+historical_discharge = Zeit-Diagramm
+historical_discharge_wq = W/Q-Diagramm
 flow_velocity = Flie\u00dfgeschwindigkeiten
 flow_velocity_export = Flie\u00dfgeschwindigkeiten Export
 bedheight_middle = Mittlere Sohlh\u00f6he
@@ -275,7 +275,7 @@
 gauge_class = Abflussklasse
 eventselect = Ereignisauswahl
 events = Ereignisse
-kmchart = W/Q Vorschau
+kmchart = W/Q-Vorschau
 
 exportATTooltip = Daten als AT Datei exportieren
 
@@ -389,7 +389,7 @@
 removeFeature = images/removeFeature.png
 getFeatureInfo = images/info.png
 getFeatureInfoTooltip = Informationswerkzeug
-getFeatureInfoWindowTitle = Informationen zu Kartenebenen
+getFeatureInfoWindowTitle = Information zur Kartenebene:
 addWMS = images/add_map.png
 printMapSettings = images/print_map_settings.png
 addWMSTooltip = Laden von Kartenebenen eines externen WMS Dienstes.
@@ -415,6 +415,9 @@
 upload_file = hochladen
 shape_file_upload = Shapedatei hochladen
 attribution = &copyIntevation GmbH 2013<br>Data &copy<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>
+wmsURLMenuItem = WMS URL
+wmsURLBoxTitle = WMS URL der Kartenebene
+requireTheme = Sie m\u00fcssen ein Thema ausw\u00e4hlen.
 
 # data cage
 waterlevels = Wasserst\u00e4nde
@@ -454,7 +457,7 @@
 land = Land
 rastermap = Rasterkarte
 background = Hintergrundkarte (WMS)
-discharge_tables_chart = WQ-Vorschau
+discharge_tables_chart = W/Q-Vorschau
 discharge_table_nn = Abflusstafeln am Pegel
 discharge_table_gauge = Abflusstafel am Pegel
 mainvalue = Hauptwerte
@@ -626,7 +629,7 @@
 gauge_river_url = https://flys-intern.intevation.de/GewaesserInfo/
 gauge_curve_link = Abflusskurve/-tafel
 discharge_timeranges = AK-Zeitr\u00e4ume
-discharge_chart = WQ-Vorschau
+discharge_chart = W/Q-Vorschau
 
 measurement_station_type = Messstellenart
 measurement_station_operator = Betreiber
@@ -659,3 +662,6 @@
 mapSource = Datenquelle
 mapDate = Ort, Datum
 mapLogo = Logo
+
+# Get Feature Info Window
+PATH = Dateipfad
--- a/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties	Wed Apr 24 15:20:50 2013 +0200
@@ -219,7 +219,7 @@
 discharge_longitudinal_section = Discharge Longitudinal Section
 floodmap = Floodmap
 historical_discharge = Time Chart
-historical_discharge_wq = WQ Chart
+historical_discharge_wq = W/Q Chart
 flow_velocity = Flow Velocity
 flow_velocity_export = Flow Velocity Export
 bedheight_middle = Middle Bed Height
@@ -275,7 +275,7 @@
 gauge_class = Gauge Class
 eventselect = Eventselection
 events = Events
-kmchart = W/Q Preview
+kmchart = W/Q-Preview
 
 chart_themepanel_header_themes = Theme
 chart_themepanel_header_actions = Actions
@@ -387,7 +387,7 @@
 removeFeature = images/removeFeature.png
 getFeatureInfo = images/info.png
 getFeatureInfoTooltip = Information Tool
-getFeatureInfoWindowTitle = Informations for Map Layers.
+getFeatureInfoWindowTitle = Information for Map Layer:
 addWMS = images/add_map.png
 printMapSettings = images/print_map_settings.png
 addWMSTooltip = Load layers from external WMS service.
@@ -412,7 +412,10 @@
 requireDGM = You need to choose a DEM.
 upload_file = upload
 shape_file_upload = Upload shapefile
-attribution = &copyIntevation GmbH 2012<br>Data &copy<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>
+attribution = &copyIntevation GmbH 2013<br>Data &copy<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>
+wmsURLMenuItem = WMS URL
+wmsURLBoxTitle = Layer WMS URL
+requireTheme = You need to choose a Layer.
 
 # data cage
 waterlevels = Waterlevels
@@ -451,7 +454,7 @@
 land = Land
 rastermap = Rastermap
 background = Background Layer (WMS)
-discharge_tables_chart = WQ preview
+discharge_tables_chart = W/Q-Preview
 discharge_table_nn = Discharge Tables at Gauge
 discharge_table_gauge = Discharge Table at Gauge
 mainvalue = Mainvalue
@@ -601,7 +604,7 @@
 gauge_river_url = https://flys-intern.intevation.de/GewaesserInfo/
 gauge_curve_link = Dischargecurve/-table
 discharge_timeranges = DC-Timeranges
-discharge_chart = WQ-Preview
+discharge_chart = W/Q-Preview
 
 measurement_station_type = Type of Measurement Station
 measurement_station_operator = Operator
@@ -609,3 +612,7 @@
 measurement_station_url = https://flys-intern.intevation.de/MessstellenInfo/
 measurement_station_info_link = Measurement Station Info
 measurement_station_gauge_name = Reference Gauge
+
+# Get Feature Info Window
+PATH = Path
+
--- a/flys-client/src/main/java/de/intevation/flys/client/client/services/GFIService.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/services/GFIService.java	Wed Apr 24 15:20:50 2013 +0200
@@ -15,7 +15,7 @@
 public interface GFIService extends RemoteService {
 
     public List<FeatureInfo> query(
-        List<Theme> themes,
+        Theme       theme,
         String      format,
         String      bbox,
         String      projection,
--- a/flys-client/src/main/java/de/intevation/flys/client/client/services/GFIServiceAsync.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/services/GFIServiceAsync.java	Wed Apr 24 15:20:50 2013 +0200
@@ -14,7 +14,7 @@
 public interface GFIServiceAsync {
 
     void query(
-        List<Theme>           themes,
+        Theme                 theme,
         String                format,
         String                bbox,
         String                projection,
--- a/flys-client/src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/ui/ThemePanel.java	Wed Apr 24 15:20:50 2013 +0200
@@ -79,7 +79,6 @@
     /** The collection view*/
     protected CollectionView view;
 
-
     /**
      * Setup Grid, navigation bar.
      * @param collection Collection for which to show themes.
@@ -158,6 +157,9 @@
         return getCollection().getThemeList(mode.getName());
     }
 
+    public ListGridRecord[] getSelectedRecords() {
+        return list.getSelectedRecords();
+    }
 
     /**
      * Registers a new OutputParameterChangeHandler.
--- a/flys-client/src/main/java/de/intevation/flys/client/client/ui/map/GetFeatureInfo.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/ui/map/GetFeatureInfo.java	Wed Apr 24 15:20:50 2013 +0200
@@ -6,6 +6,7 @@
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
 import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
 
 import org.gwtopenmaps.openlayers.client.Map;
 import org.gwtopenmaps.openlayers.client.LonLat;
@@ -17,6 +18,9 @@
 import de.intevation.flys.client.client.FLYSConstants;
 import de.intevation.flys.client.client.services.GFIService;
 import de.intevation.flys.client.client.services.GFIServiceAsync;
+import de.intevation.flys.client.shared.model.FacetRecord;
+import de.intevation.flys.client.shared.model.Theme;
+import de.intevation.flys.client.shared.model.AttributedTheme;
 import de.intevation.flys.client.client.ui.ThemePanel;
 
 
@@ -56,12 +60,12 @@
     }
 
 
-    protected void newGetFeatureInfoWindow(List<FeatureInfo> features) {
+    protected void newGetFeatureInfoWindow(List<FeatureInfo> features, String title) {
         if (gfiWindow != null) {
             gfiWindow.destroy();
         }
 
-        gfiWindow = new GetFeatureInfoWindow(features);
+        gfiWindow = new GetFeatureInfoWindow(features, title);
         gfiWindow.show();
     }
 
@@ -71,22 +75,35 @@
         LonLat lonlat = e.getLonLat();
         Pixel  pixel  = map.getPixelFromLonLat(lonlat);
 
-        gfiService.query(
-            themePanel.getThemeList().getActiveThemes(),
-            infoFormat,
-            map.getExtent().toString(),
-            map.getProjection(),
-            (int) map.getSize().getHeight(),
-            (int) map.getSize().getWidth(),
-            pixel.x(), pixel.y(),
-            new AsyncCallback<List<FeatureInfo>>() {
-            public void onFailure(Throwable e) {
-                SC.warn(MSG.getString(e.getMessage()));
-            }
+        if (themePanel.getSelectedRecords().length == 0) {
+            SC.say(MSG.requireTheme());
+        }
 
-            public void onSuccess(List<FeatureInfo> features) {
-                newGetFeatureInfoWindow(features);
-            }
-        });
+        for (ListGridRecord rec : themePanel.getSelectedRecords()) {
+            Theme act_theme = ((FacetRecord)rec).getTheme();
+            final AttributedTheme at = (AttributedTheme)act_theme;
+            gfiService.query(
+                act_theme,
+                infoFormat,
+                map.getExtent().toString(),
+                map.getProjection(),
+                (int) map.getSize().getHeight(),
+                (int) map.getSize().getWidth(),
+                pixel.x(), pixel.y(),
+                new AsyncCallback<List<FeatureInfo>>() {
+                    @Override
+                    public void onFailure(Throwable e) {
+                        SC.warn(MSG.getString(e.getMessage()));
+                    }
+
+                    @Override
+                    public void onSuccess(List<FeatureInfo> features) {
+                        if (features != null && !features.isEmpty())
+                            newGetFeatureInfoWindow(features, at.getAttr("description"));
+                    }
+                }
+            );
+            break; // More intelligent handling when more then one is selected
+        }
     }
 }
--- a/flys-client/src/main/java/de/intevation/flys/client/client/ui/map/GetFeatureInfoWindow.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/ui/map/GetFeatureInfoWindow.java	Wed Apr 24 15:20:50 2013 +0200
@@ -4,15 +4,19 @@
 
 import com.smartgwt.client.widgets.Label;
 import com.smartgwt.client.widgets.Window;
-import com.smartgwt.client.widgets.layout.HLayout;
 import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.viewer.DetailViewer;
+import com.smartgwt.client.widgets.viewer.DetailViewerField;
+import com.smartgwt.client.widgets.viewer.DetailViewerRecord;
 
 import de.intevation.flys.client.client.FLYSConstants;
 import de.intevation.flys.client.shared.model.FeatureInfo;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.MissingResourceException;
 
 import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
 import org.gwtopenmaps.openlayers.client.util.Attributes;
@@ -25,13 +29,16 @@
 
     protected List<FeatureInfo> features;
 
+    protected String title;
+
 
     public static final int ROW_HEIGHT = 25;
 
 
-    public GetFeatureInfoWindow(List<FeatureInfo> features) {
+    public GetFeatureInfoWindow(List<FeatureInfo> features, String title) {
         super();
         this.features = features;
+        this.title = title;
 
         initLayout();
     }
@@ -41,44 +48,60 @@
         VLayout root = new VLayout();
 
         for (FeatureInfo feature: features) {
-            root.addMember(createFeatureRow(feature));
+            // Currently this should alway be only one
+            root.addMember(createFeatureViewer(feature));
+            setTitle(MSG.getFeatureInfoWindowTitle() + " " + title);
         }
 
         addItem(root);
 
         setWidth(500);
-        setHeight(500);
-        setTitle(MSG.getFeatureInfoWindowTitle());
+        setHeight(300);
 
-        setIsModal(true);
-        setShowModalMask(true);
+        setIsModal(false);
+//        setShowModalMask(true);
 
         centerInPage();
     }
 
 
-    protected HLayout createFeatureRow(FeatureInfo feature) {
-        HLayout r = new HLayout();
-        r.setHeight(ROW_HEIGHT);
-        r.setStyleName("featureinfo-row");
-        r.setMembersMargin(5);
-
-        Label l = new Label("Layer: " + feature.getLayername());
-        l.setHeight(ROW_HEIGHT);
-        l.setWrap(false);
-        r.addMember(l);
+    protected DetailViewer createFeatureViewer(FeatureInfo feature) {
+        DetailViewer detailViewer = new DetailViewer();
+        detailViewer.setWidth(487);
 
         Map<String, String> attrs = feature.getAttrs();
         Set<Map.Entry<String, String>> entries = attrs.entrySet();
+        List <DetailViewerField> fields = new ArrayList<DetailViewerField>();
+        DetailViewerRecord dr = new DetailViewerRecord();
+
+        DetailViewerField path_field = null; // Make sure path is always the last element
 
         for (Map.Entry<String, String> entry: entries) {
-            Label attr = new Label(entry.getKey() + ": " + entry.getValue());
-            attr.setHeight(ROW_HEIGHT);
+            String localized;
+            try {
+                localized = MSG.getString(entry.getKey());
+            } catch (MissingResourceException mre) {
+                localized = entry.getKey();
+//                We filter unwanted information by localization
+//                Uncomment to filter out unlocalized elements
+//                continue;
+            }
+            if (entry.getKey().equals("PATH")) {
+                path_field = new DetailViewerField(entry.getKey(), localized);
+            } else {
+                fields.add(new DetailViewerField(entry.getKey(), localized));
+            }
+            dr.setAttribute(entry.getKey(), entry.getValue());
+        }
+        if (path_field != null)
+            fields.add(path_field);
 
-            r.addMember(attr);
-        }
+        DetailViewerField[] fieldArray = new DetailViewerField[fields.size()];
+        detailViewer.setFields(fields.toArray(fieldArray));
+        detailViewer.setData(new DetailViewerRecord[]{dr});
+        detailViewer.setCanSelectText(true);
 
-        return r;
+        return detailViewer;
     }
 
 
--- a/flys-client/src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/ui/map/MapOutputTab.java	Wed Apr 24 15:20:50 2013 +0200
@@ -263,6 +263,10 @@
         if (map != null) {
             GWT.log("Add new layer '" + layer.getName() + "' to map.");
             map.addLayer(layer);
+            if (layer instanceof WMS) {
+                wmsUrls.put(layer.getName(),
+                        ((WMS)layer).getFullRequestString(new WMSParams(), null));
+            }
         }
     }
 
@@ -454,7 +458,6 @@
         if (url == null || layers == null) {
             return null;
         }
-        this.wmsUrls.put(name, url);
 
         WMSParams params = new WMSParams();
         params.setLayers(layers);
@@ -473,7 +476,8 @@
         WMS wms = new WMS(layers, url, params, opts);
         wms.setIsVisible(at.getActive() == 1);
         wms.setIsBaseLayer(false);
-
+        // We can't set the full_url attribute here because map is not set
+        // at.addAttr("full_url", wms.getFullRequestString(params, null));
         return wms;
     }
 
--- a/flys-client/src/main/java/de/intevation/flys/client/client/ui/map/MapThemePanel.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/ui/map/MapThemePanel.java	Wed Apr 24 15:20:50 2013 +0200
@@ -176,7 +176,7 @@
         if (layerZoom != null) {
             menu.addItem(layerZoom);
         }
-        //menu.addItem(createMapURLItem(records));
+        menu.addItem(createMapURLItem(records));
 
         return menu;
     }
@@ -186,11 +186,12 @@
         final FacetRecord     fr = (FacetRecord) records[0];
         final AttributedTheme at = (AttributedTheme) fr.getTheme();
 
-        MenuItem item = new MenuItem("WMS URL");
+        MenuItem item = new MenuItem(MSG.wmsURLMenuItem());
         item.addClickHandler(new ClickHandler() {
             @Override
             public void onClick(MenuItemClickEvent evt) {
-                SC.say(at.getAttr("url"));
+                String url = getMapOutputTab().wmsUrls().get(at.getAttr("layers"));
+                SC.say(MSG.wmsURLBoxTitle(), url);
             }
         });
 
--- a/flys-client/src/main/java/de/intevation/flys/client/server/GFIServiceImpl.java	Wed Apr 24 15:17:45 2013 +0200
+++ b/flys-client/src/main/java/de/intevation/flys/client/server/GFIServiceImpl.java	Wed Apr 24 15:20:50 2013 +0200
@@ -47,7 +47,7 @@
 
 
     /**
-     * @param themes
+     * @param theme
      * @param format
      * @param bbox
      * @param height
@@ -58,7 +58,7 @@
      * @return
      */
     public List<FeatureInfo> query(
-        List<Theme> themes,
+        Theme       theme,
         String      format,
         String      bbox,
         String      projection,
@@ -71,7 +71,7 @@
         logger.info("GFIServiceImpl.query");
 
         String path = createGetFeautureInfoURL(
-            themes, format, bbox, projection, height, width, x, y);
+            theme, format, bbox, projection, height, width, x, y);
 
         logger.debug("URL=" + path);
 
@@ -96,7 +96,7 @@
 
     /**
      * @param map
-     * @param themes
+     * @param theme
      * @param format
      * @param x
      * @param y
@@ -104,7 +104,7 @@
      * @return
      */
     protected String createGetFeautureInfoURL(
-        List<Theme> themes,
+        Theme       theme,
         String      infoFormat,
         String      bbox,
         String      projection,
@@ -114,13 +114,13 @@
         int         y
     ) throws ServerException
     {
-        String url = getUrl(themes);
+        String url = getUrl(theme);
 
         if (url == null || url.length() == 0) {
             throw new ServerException(ERR_NO_VALID_GFI_URL);
         }
 
-        String layers = createLayersString(themes);
+        String layers = ((AttributedTheme)theme).getAttr("layers");
 
         StringBuilder sb = new StringBuilder();
         sb.append(url);
@@ -149,36 +149,13 @@
     }
 
 
-    protected String getUrl(List<Theme> themes) {
-        for (Theme t: themes) {
-            AttributedTheme attr = (AttributedTheme) t;
-
-            if (attr.getAttrAsBoolean("queryable")) {
-                return attr.getAttr("url");
-            }
-        }
-
-        return null;
-    }
-
+    protected String getUrl(Theme theme) {
+        AttributedTheme attr = (AttributedTheme) theme;
 
-    protected String createLayersString(List<Theme> themes) {
-        StringBuilder sb = new StringBuilder();
-        boolean first = true;
-
-        for (Theme theme: themes) {
-            AttributedTheme layer = (AttributedTheme) theme;
-            if (layer.getAttrAsBoolean("queryable")) {
-                if (!first) {
-                    sb.append(",");
-                }
-
-                sb.append(layer.getAttr("layers"));
-                first = false;
-            }
+        if (attr.getAttrAsBoolean("queryable")) {
+            return attr.getAttr("url");
         }
-
-        return sb.toString();
+        return null;
     }
 
 

http://dive4elements.wald.intevation.org