changeset 9237:972e10522ed6

salix.supraregional ui
author gernotbelger
date Tue, 10 Jul 2018 11:24:12 +0200
parents b515ed950d39
children 1c756bfee472
files artifacts/doc/conf/artifacts/uinfo.xml artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixZone.java artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SupraRegionalTableState.java artifacts/src/main/resources/messages.properties artifacts/src/main/resources/messages_de.properties gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractUIProvider.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DistancePanel.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DistancePanelInputHelper.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/PanelHelper.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/UIProviderFactory.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/uinfo/SupraRegionalTablePanel.java gwt-client/src/main/java/org/dive4elements/river/client/shared/model/SalixZone.java
diffstat 15 files changed, 1111 insertions(+), 185 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/artifacts/uinfo.xml	Fri Jul 06 17:56:18 2018 +0200
+++ b/artifacts/doc/conf/artifacts/uinfo.xml	Tue Jul 10 11:24:12 2018 +0200
@@ -78,6 +78,18 @@
 
     <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
       <from state="state.uinfo.scenario_type" />
+      <to state="state.uinfo.salix.supraregional" />
+      <condition data="scenario_selection" value="scenarioType.option2" operator="equal" />
+    </transition>
+
+    <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
+      <from state="state.uinfo.salix.supraregional" />
+      <to state="state.uinfo.salix_line" />
+      <condition data="calculation_mode" value="uinfo_salix_line" operator="equal" />
+    </transition>
+    
+    <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
+      <from state="state.uinfo.scenario_type" />
       <to state="state.uinfo.year_epoch" />
       <condition data="scenario_selection" value="scenarioType.option3" operator="equal" />
     </transition>
@@ -85,7 +97,9 @@
     <state id="state.uinfo.year_epoch" description="state.uinfo.year_epoch" state="org.dive4elements.river.artifacts.uinfo.salix.YearChoice" helpText="help.state.uinfo.year">
       <data name="ye_select" type="String" />
     </state>
-
+    <state id="state.uinfo.salix.supraregional" description="state.uinfo.salix.supraregional" state="org.dive4elements.river.artifacts.uinfo.salix.SupraRegionalTableState" helpText="help.state.uinfo.salix">
+      <data name="supraregional_table" type="String" />
+    </state>
 
     <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
       <from state="state.uinfo.year_epoch" />
@@ -343,7 +357,7 @@
       <to state="state.uinfo.vegetation_zones.table" />
       <condition data="calculation_mode" value="uinfo_vegetation_zones" operator="equal" />
     </transition>
-
+ 
 
 
     <state id="state.uinfo.vegetation_zones.table" description="state.uinfo.vegetation_zones.table" state="org.dive4elements.river.artifacts.uinfo.vegetationzones.VegetationZonesTableEditState" helpText="help.state.uinfo.vegetation_zones">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixZone.java	Tue Jul 10 11:24:12 2018 +0200
@@ -0,0 +1,154 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.uinfo.salix;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * @author Domenico Nardi Tironi
+ *
+ */
+public class SalixZone implements Comparable<SalixZone> {
+
+    // IMMER ABGLEICHEN Server Client SalixZone.java
+    private static final String TABLE_CELL_SEPARATOR = "TABLE_CELL_SEPARATOR";
+    private static final String TABLE_ROW_SEPARATOR = "TABLE_ROW_SEPARATOR";
+    private final int dwsplValue;
+    private final double fromKm;
+    private final double toKm;
+
+    private final static double DELTA = 0.0001;
+
+    public static List<SalixZone> parse(final String zonesRaw) {
+        final List<SalixZone> resultList = new ArrayList<>();
+
+        final List<String[]> results = new ArrayList<>();
+        if (zonesRaw.contains(TABLE_ROW_SEPARATOR)) {
+            final String[] rows = zonesRaw.split(TABLE_ROW_SEPARATOR);
+            for (final String row : rows) {
+                if (row.contains(TABLE_CELL_SEPARATOR)) {
+                    final String[] result = row.split(TABLE_CELL_SEPARATOR);
+                    results.add(result);
+                }
+            }
+        }
+        for (final String[] zone : results) {
+            final SalixZone helper = new SalixZone(Integer.valueOf(zone[0]), Double.valueOf(zone[1]), Double.valueOf(zone[2]));
+            resultList.add(helper);
+        }
+        return resultList;
+    }
+
+    public static SalixZone createFromTableEntry(final String dwspl, final String from, final String to) {
+        return new SalixZone(Integer.valueOf(dwspl), Double.valueOf(from), Double.valueOf(to)); // Error-Handling?
+    }
+
+    private SalixZone(final int dwsplValue, final double fromKm, final double toKm) {
+        this.dwsplValue = dwsplValue;
+        this.fromKm = fromKm;
+        this.toKm = toKm;
+    }
+
+    public Double getToKm() {
+        return this.toKm;
+    }
+
+    public int getDwsplValue() {
+        return this.dwsplValue;
+    }
+
+    public Double getFromKm() {
+        return this.fromKm;
+    }
+
+    public static final String parseListToDataString(final List<SalixZone> list) {
+
+        java.util.Collections.sort(list);
+        final StringBuilder builder = new StringBuilder();
+        for (final SalixZone zone : list) {
+            builder.append(zone.getDwsplValue());
+            builder.append(TABLE_CELL_SEPARATOR);
+            builder.append(zone.getFromKm());
+            builder.append(TABLE_CELL_SEPARATOR);
+            builder.append(zone.getToKm());
+            builder.append(TABLE_ROW_SEPARATOR);
+        }
+        return builder.toString();
+    }
+
+    public static final boolean zonesAreOverlapping(final List<SalixZone> list) {
+        for (final SalixZone zone : list) {
+            for (final SalixZone zoneOther : list) {
+                if (zone != zoneOther) {
+                    final boolean overlaps = zone.overlaps(zoneOther);
+                    if (overlaps) {
+                        return overlaps; // cancel. only one zone has to overlap
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public static final boolean hasGaps(final List<SalixZone> list, final double lower, final double upper) {
+
+        if (((upper - lower) > DELTA) && list.size() == 0)
+            return true;
+
+        final TreeSet<SalixZone> treeList = new TreeSet<SalixZone>();
+        treeList.addAll(list);
+        double lowerCompare = lower + DELTA;
+        for (final SalixZone zone : treeList) {
+            if (zone.getLowerFromTo() > lowerCompare) {
+                return true;
+            }
+            lowerCompare = zone.getUpperFromTo() + DELTA;
+        }
+        if ((lowerCompare + DELTA) < upper)
+            return true; // am Ende nicht geschlossen
+
+        return false;
+    }
+
+    private Double getLowerFromTo() {
+        return this.fromKm < this.toKm ? this.fromKm : this.toKm; // Math. is forbidden :-(
+    }
+
+    private Double getUpperFromTo() {
+        return this.fromKm > this.toKm ? this.fromKm : this.toKm;// Math. is forbidden :-(
+    }
+
+    private boolean overlaps(final SalixZone otherZone) {
+        final double otherLower = otherZone.getLowerFromTo();
+        final double otherUpper = otherZone.getUpperFromTo();
+
+        final double upper = getUpperFromTo() - DELTA;
+        final double lower = getLowerFromTo() + DELTA;
+        final double otherSchwerpunkt = (otherLower + otherUpper) / 2;
+        if ((otherUpper < upper && otherUpper > lower)) {
+            return true;
+        } else if (otherLower > lower && otherLower < upper) {
+            return true;
+        } else if (otherSchwerpunkt > lower && otherSchwerpunkt < upper) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int compareTo(final SalixZone o) {
+        final int basicCompare = this.getLowerFromTo().compareTo(o.getLowerFromTo());
+        return basicCompare;
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SupraRegionalTableState.java	Tue Jul 10 11:24:12 2018 +0200
@@ -0,0 +1,54 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.uinfo.salix;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.w3c.dom.Element;
+
+/**
+ * @author Domenico Nardi Tironi
+ */
+public class SupraRegionalTableState extends DefaultState {
+
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    protected String getUIProvider() {
+        return "uinfo.salix.supraregional.table";
+    }
+
+    @Override
+    protected Element[] createItems(final ElementCreator cr, final Artifact artifact, final String name, final CallContext context) {
+        final String datakey = "supraregional_table";
+        if (name.equals(datakey)) {
+
+            final D4EArtifact flys = (D4EArtifact) artifact;
+            final String s = flys.getDataAsString(datakey);
+
+            final RangeAccess ra = new RangeAccess((D4EArtifact) artifact);
+
+            final Element item = createItem(cr, new String[] { "salix_zones", s });
+            final Element min = createItem(cr, new String[] { "salix_zones_min", new Double(ra.getFrom()).toString() });
+
+            final Element max = createItem(cr, new String[] { "salix_zones_max", new Double(ra.getTo()).toString() });
+
+            return new Element[] { min, max, item };
+
+        }
+
+        return new Element[] {};
+    }
+
+}
\ No newline at end of file
--- a/artifacts/src/main/resources/messages.properties	Fri Jul 06 17:56:18 2018 +0200
+++ b/artifacts/src/main/resources/messages.properties	Tue Jul 10 11:24:12 2018 +0200
@@ -76,6 +76,7 @@
 state.minfo.m3_per_a = m\u00b3/a
 
 state.uinfo.vegetation_zones.table = Einteilung der Vegetationszonen und \u00dcberflutungsdauern (\u00dcFD)
+state.uinfo.salix.supraregional = \u00dcberregional wirkende Eingriffe
 
 state.uinfo.year_totalepoch = Year/Gesamter Zeitraum
 state.uinfo.year_epoch = Year/Epoch
--- a/artifacts/src/main/resources/messages_de.properties	Fri Jul 06 17:56:18 2018 +0200
+++ b/artifacts/src/main/resources/messages_de.properties	Tue Jul 10 11:24:12 2018 +0200
@@ -76,6 +76,7 @@
 state.minfo.m3_per_a = m\u00b3/a
 
 state.uinfo.vegetation_zones.table = Einteilung der Vegetationszonen und \u00dcberflutungsdauern (\u00dcFD)
+state.uinfo.salix.supraregional = \u00dcberregional wirkende Eingriffe
 
 state.uinfo.year_totalepoch = Jahr/Gesamter Zeitraum
 state.uinfo.year_epoch = Jahr/Epoche
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Fri Jul 06 17:56:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Tue Jul 10 11:24:12 2018 +0200
@@ -1556,4 +1556,14 @@
     String sinfo_sounding_waterlevel_select_waterlevel();
 
     String sinfo_deactivate_intelligent_datacord();
+
+    String uinfo_salix_dmwspl_short();
+
+    String uinfo_salix_invalid_double();
+
+    String uinfo_salix_km_limit_exceed();
+
+    String uinfo_salix_km_overlap();
+
+    String uinfo_salix_km_has_gaps();
 }
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Fri Jul 06 17:56:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Tue Jul 10 11:24:12 2018 +0200
@@ -817,6 +817,7 @@
 sinfo_flood_height = H\u00f6he Infrastruktur BWaStr
 
 uinfo = U-INFO
+uinfo_salix_dmwspl_short = \u0394MW [cm]
 uinfo_inundation_duration_export = \u00dcberflutungsdauern Export
 uinfo_salix_line_export = Salix-Linie Export
 uinfo_vegetation_zones_export = Vegetationszonen Export
@@ -829,6 +830,10 @@
 uinfo_vegetation_zones_validation_range = Werte m\u00fcssen zwischen 0 und 365 liegen.
 uinfo_vegetation_zones_validation_from_greater_to = Der Wert f\u00fcr "\u00dcfd von" muss kleiner als "\u00dcfd bis".
 uinfo_vegetation_zones_invalid_integer = Nur Ganzzahlen erlaubt.
+uinfo_salix_invalid_double = Nur Zahlen erlaubt.
+uinfo_salix_km_limit_exceed = Die Zahl ist au\u00dferhalb des g\u00fcltigen km-Bereichs.
+uinfo_salix_km_overlap = Km-Bereiche \u00fcperlappen.
+uinfo_salix_km_has_gaps = Der Km-Bereich ist nicht vollst. belegt.
 
 bundu = Betrieb und Unterhaltung 
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Fri Jul 06 17:56:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Tue Jul 10 11:24:12 2018 +0200
@@ -817,6 +817,7 @@
 sinfo_flood_height = H\u00f6he Infrastruktur BWaStr
 
 uinfo = U-INFO
+uinfo_salix_dmwspl_short = \u0394MW [cm]
 uinfo_inundation_duration_export = \u00dcberflutungsdauern Export
 uinfo_salix_line_export = Salix-Linie Export
 uinfo_vegetation_zones_export = Vegetationszonen Export
@@ -829,6 +830,10 @@
 uinfo_vegetation_zones_validation_range = Werte m\u00fcssen zwischen 0 und 365 liegen.
 uinfo_vegetation_zones_validation_from_greater_to = Der Wert f\u00fcr "\u00dcfd von" muss kleiner als "\u00dcfd bis".
 uinfo_vegetation_zones_invalid_integer = Nur Ganzzahlen erlaubt.
+uinfo_salix_invalid_double = Nur Zahlen erlaubt.
+uinfo_salix_km_limit_exceed = Die Zahl ist au\u00dferhalb des g\u00fcltigen km-Bereichs.
+uinfo_salix_km_overlap = Km-Bereiche \u00fcperlappen.
+uinfo_salix_km_has_gaps = Der Km-Bereich ist nicht vollst. belegt.
 
 bundu = Betrieb und Unterhaltung
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractUIProvider.java	Fri Jul 06 17:56:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/AbstractUIProvider.java	Tue Jul 10 11:24:12 2018 +0200
@@ -331,5 +331,11 @@
      * @return the selected data.
      */
     protected abstract Data[] getData();
+
+    protected String getRiverName() {
+        final ArtifactDescription adescr = this.artifact.getArtifactDescription();
+        return adescr.getRiver();
+    }
+
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DistancePanel.java	Fri Jul 06 17:56:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DistancePanel.java	Tue Jul 10 11:24:12 2018 +0200
@@ -9,18 +9,9 @@
 package org.dive4elements.river.client.client.ui;
 
 import java.util.ArrayList;
-import java.util.LinkedHashMap;
 import java.util.List;
 
-import org.dive4elements.river.client.client.Config;
 import org.dive4elements.river.client.client.FLYSConstants;
-import org.dive4elements.river.client.client.event.FilterHandler;
-import org.dive4elements.river.client.client.event.RangeFilterEvent;
-import org.dive4elements.river.client.client.event.StringFilterEvent;
-import org.dive4elements.river.client.client.ui.range.DistanceInfoDataSource;
-import org.dive4elements.river.client.client.ui.range.LocationsTable;
-import org.dive4elements.river.client.client.ui.range.RangeTable;
-import org.dive4elements.river.client.shared.model.ArtifactDescription;
 import org.dive4elements.river.client.shared.model.Data;
 import org.dive4elements.river.client.shared.model.DataItem;
 import org.dive4elements.river.client.shared.model.DataList;
@@ -29,34 +20,22 @@
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.i18n.client.NumberFormat;
-import com.smartgwt.client.data.AdvancedCriteria;
-import com.smartgwt.client.data.Criteria;
-import com.smartgwt.client.data.Criterion;
 import com.smartgwt.client.data.Record;
-import com.smartgwt.client.types.Alignment;
-import com.smartgwt.client.types.OperatorId;
 import com.smartgwt.client.util.SC;
 import com.smartgwt.client.widgets.Canvas;
 import com.smartgwt.client.widgets.Label;
-import com.smartgwt.client.widgets.form.DynamicForm;
-import com.smartgwt.client.widgets.form.fields.SelectItem;
 import com.smartgwt.client.widgets.form.fields.StaticTextItem;
 import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
 import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
-import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
-import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
 import com.smartgwt.client.widgets.grid.ListGrid;
 import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
 import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
 import com.smartgwt.client.widgets.layout.HLayout;
 import com.smartgwt.client.widgets.layout.VLayout;
-import com.smartgwt.client.widgets.tab.Tab;
 import com.smartgwt.client.widgets.tab.TabSet;
-import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
-import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;
 
 /** Panel to allow input of distance for calculation range. */
-public class DistancePanel extends AbstractUIProvider implements BlurHandler, FilterHandler {
+public class DistancePanel extends AbstractUIProvider implements BlurHandler {
 
     private static final long serialVersionUID = -883142387908664588L;
 
@@ -68,8 +47,8 @@
 
     protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
 
-    protected RangeTable distancesTable;
-    protected LocationsTable locationsTable;
+    // protected RangeTable distancesTable;
+    // protected LocationsTable locationsTable;
 
     protected DoubleRangePanel distancePanel;
 
@@ -299,11 +278,6 @@
         return new DefaultData(field, null, null, new DataItem[] { item });
     }
 
-    @Override
-    public void onBlur(final BlurEvent event) {
-        this.distancePanel.validateForm();
-    }
-
     protected void initMinMaxValues(final DataList data) {
         final Data f = getData(data.getAll(), getLowerField());
         final Data t = getData(data.getAll(), getUpperField());
@@ -393,19 +367,10 @@
     }
 
     protected void initHelperPanel() {
-        this.distancesTable = new RangeTable();
-        this.locationsTable = new LocationsTable();
 
-        final Config config = Config.getInstance();
-        final String url = config.getServerUrl();
-        final String river = getRiverName();
+        final DistancePanelInputHelper helper = new DistancePanelInputHelper(this.MSG, this.helperContainer, this.getRiverName());
 
-        this.distancesTable.setAutoFetchData(true);
-        this.locationsTable.setAutoFetchData(true);
-        this.distancesTable.setDataSource(new DistanceInfoDataSource(url, river, "distances"));
-        this.locationsTable.setDataSource(new DistanceInfoDataSource(url, river, "locations"));
-
-        this.distancesTable.addRecordClickHandler(new RecordClickHandler() {
+        helper.getDistancesTable().addRecordClickHandler(new RecordClickHandler() {
             @Override
             public void onRecordClick(final RecordClickEvent e) {
                 final Record r = e.getRecord();
@@ -423,7 +388,7 @@
             }
         });
 
-        this.locationsTable.addRecordClickHandler(new RecordClickHandler() {
+        helper.getLocationsTable().addRecordClickHandler(new RecordClickHandler() {
             @Override
             public void onRecordClick(final RecordClickEvent e) {
                 final Record r = e.getRecord();
@@ -447,151 +412,11 @@
             }
         });
 
-        this.tabs = new TabSet();
-        this.tabs.setWidth100();
-        this.tabs.setHeight100();
-
-        final Tab locations = new Tab(this.MSG.locations());
-        final Tab distances = new Tab(this.MSG.distance());
-
-        locations.setPane(this.locationsTable);
-        distances.setPane(this.distancesTable);
-
-        this.tabs.addTab(locations, 0);
-        this.tabs.addTab(distances, 1);
-
-        this.filterResultCount = new StaticTextItem(this.MSG.resultCount());
-        this.filterResultCount.setTitleAlign(Alignment.LEFT);
-        this.filterResultCount.setTitleStyle("color: #000");
-
-        this.filterDescription = new TableFilter();
-        this.filterDescription.setHeight("30px");
-        this.filterDescription.addFilterHandler(this);
-
-        this.filterRange = new RangeTableFilter();
-        this.filterRange.setHeight("30px");
-        this.filterRange.addFilterHandler(this);
-        this.filterRange.setVisible(false);
-
-        final SelectItem filterCriteria = new SelectItem();
-        filterCriteria.setShowTitle(false);
-        filterCriteria.setWidth(100);
-        filterCriteria.addChangedHandler(new ChangedHandler() {
-            @Override
-            public void onChanged(final ChangedEvent e) {
-                if (e.getValue().toString().equals("range")) {
-                    DistancePanel.this.filterRange.setVisible(true);
-                    DistancePanel.this.filterDescription.setVisible(false);
-                    DistancePanel.this.filterDescription.clear();
-                } else {
-                    DistancePanel.this.filterRange.setVisible(false);
-                    DistancePanel.this.filterRange.clear();
-                    DistancePanel.this.filterDescription.setVisible(true);
-                }
-            }
-        });
-
-        final LinkedHashMap<String, String> filterMap = new LinkedHashMap<String, String>();
-        filterMap.put("description", this.MSG.description());
-        filterMap.put("range", this.MSG.range());
-        filterCriteria.setValueMap(filterMap);
-        filterCriteria.setValue("description");
-
-        final DynamicForm form = new DynamicForm();
-        form.setFields(filterCriteria);
-
-        final DynamicForm form2 = new DynamicForm();
-        form2.setFields(this.filterResultCount);
-
-        final HLayout filterLayout = new HLayout();
-        filterLayout.addMember(form);
-        filterLayout.addMember(this.filterDescription);
-        filterLayout.addMember(this.filterRange);
-        filterLayout.setHeight(30);
-        this.tabs.addTabSelectedHandler(new TabSelectedHandler() {
-            @Override
-            public void onTabSelected(final TabSelectedEvent evt) {
-                DistancePanel.this.filterDescription.clear();
-                DistancePanel.this.filterRange.clear();
-                DistancePanel.this.filterResultCount.setValue("");
-
-                final Canvas c = evt.getTabPane();
-                if (c instanceof ListGrid) {
-                    DistancePanel.this.currentFiltered = (ListGrid) c;
-                }
-            }
-        });
-
-        this.helperContainer.addMember(this.tabs);
-        this.helperContainer.addMember(filterLayout);
-        this.helperContainer.addMember(form2);
     }
 
     @Override
-    public void onFilterCriteriaChanged(final StringFilterEvent event) {
-        final String search = event.getFilter();
-
-        if (search != null && search.length() > 0) {
-            final Criteria c = new Criteria("description", search);
-            this.locationsTable.filterData(c);
-            this.distancesTable.filterData(c);
-            this.filterResultCount.setValue(this.currentFiltered.getRecords().length);
-        } else {
-            this.locationsTable.clearCriteria();
-            this.distancesTable.clearCriteria();
-            this.filterResultCount.setValue("");
-        }
-    }
-
-    @Override
-    public void onFilterCriteriaChanged(final RangeFilterEvent event) {
-        final Float from = event.getFrom() - 0.001f;
-        final Float to = event.getTo() + 0.001f;
-
-        Criterion combinedFilter = null;
-        Criterion locationFilter = null;
-
-        if (from.equals(Float.NaN) && to.equals(Float.NaN)) {
-            this.locationsTable.clearCriteria();
-            this.distancesTable.clearCriteria();
-            this.filterResultCount.setValue("");
-            return;
-        }
-
-        if (from.equals(Float.NaN)) {
-            combinedFilter = new Criterion("to", OperatorId.LESS_OR_EQUAL, to);
-
-            locationFilter = new Criterion("from", OperatorId.LESS_OR_EQUAL, to);
-
-            this.locationsTable.filterData(locationFilter);
-            this.distancesTable.filterData(combinedFilter);
-            this.filterResultCount.setValue(this.currentFiltered.getRecords().length);
-            return;
-        }
-
-        if (to.equals(Float.NaN)) {
-            combinedFilter = new Criterion("from", OperatorId.GREATER_OR_EQUAL, from);
-        } else {
-            final AdvancedCriteria c1 = new AdvancedCriteria(OperatorId.AND,
-                    new Criterion[] { new Criterion("from", OperatorId.GREATER_OR_EQUAL, from), new Criterion("from", OperatorId.LESS_OR_EQUAL, to) });
-
-            final AdvancedCriteria c2 = new AdvancedCriteria(OperatorId.AND,
-                    new Criterion[] { new Criterion("to", OperatorId.GREATER_OR_EQUAL, from), new Criterion("to", OperatorId.LESS_OR_EQUAL, to) });
-
-            final AdvancedCriteria c3 = new AdvancedCriteria(OperatorId.AND,
-                    new Criterion[] { new Criterion("from", OperatorId.LESS_OR_EQUAL, to), new Criterion("to", OperatorId.GREATER_OR_EQUAL, from) });
-
-            combinedFilter = new AdvancedCriteria(OperatorId.OR, new Criterion[] { c1, c2, c3 });
-        }
-        this.locationsTable.filterData(combinedFilter);
-        this.distancesTable.filterData(combinedFilter);
-        this.filterResultCount.setValue(this.currentFiltered.getRecords().length);
-
-    }
-
-    protected String getRiverName() {
-        final ArtifactDescription adescr = this.artifact.getArtifactDescription();
-        return adescr.getRiver();
+    public void onBlur(final BlurEvent event) {
+        this.distancePanel.validateForm();
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DistancePanelInputHelper.java	Tue Jul 10 11:24:12 2018 +0200
@@ -0,0 +1,229 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.client.client.ui;
+
+import java.util.LinkedHashMap;
+
+import org.dive4elements.river.client.client.Config;
+import org.dive4elements.river.client.client.FLYSConstants;
+import org.dive4elements.river.client.client.event.FilterHandler;
+import org.dive4elements.river.client.client.event.RangeFilterEvent;
+import org.dive4elements.river.client.client.event.StringFilterEvent;
+import org.dive4elements.river.client.client.ui.range.DistanceInfoDataSource;
+import org.dive4elements.river.client.client.ui.range.LocationsTable;
+import org.dive4elements.river.client.client.ui.range.RangeTable;
+
+import com.smartgwt.client.data.AdvancedCriteria;
+import com.smartgwt.client.data.Criteria;
+import com.smartgwt.client.data.Criterion;
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.types.OperatorId;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.SelectItem;
+import com.smartgwt.client.widgets.form.fields.StaticTextItem;
+import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.tab.Tab;
+import com.smartgwt.client.widgets.tab.TabSet;
+import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
+import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;
+
+/** Panel to allow input of distance for calculation range. */
+public class DistancePanelInputHelper implements FilterHandler {
+
+    private RangeTable distancesTable;
+    private LocationsTable locationsTable;
+    private TabSet tabs;
+    private StaticTextItem filterResultCount;
+    private TableFilter filterDescription;
+    private RangeTableFilter filterRange;
+    private ListGrid currentFiltered;
+    protected final VLayout helperContainer;
+    private final FLYSConstants MSG;
+    private final String river;
+
+    public DistancePanelInputHelper(final FLYSConstants MSG, final VLayout helperContainer, final String river) {
+        this.MSG = MSG;
+        this.helperContainer = helperContainer;
+        this.river = river;
+
+        initHelperPanel(); // copy from DistancePanel
+    }
+
+    protected void initHelperPanel() {
+        this.distancesTable = new RangeTable();
+        this.locationsTable = new LocationsTable();
+
+        final Config config = Config.getInstance();
+        final String url = config.getServerUrl();
+        // final String river = getRiverName();
+
+        this.distancesTable.setAutoFetchData(true);
+        this.locationsTable.setAutoFetchData(true);
+        this.distancesTable.setDataSource(new DistanceInfoDataSource(url, this.river, "distances"));
+        this.locationsTable.setDataSource(new DistanceInfoDataSource(url, this.river, "locations"));
+
+        // recordClickHandlers were here
+
+        this.tabs = new TabSet();
+        this.tabs.setWidth100();
+        this.tabs.setHeight100();
+
+        final Tab locations = new Tab(this.MSG.locations());
+        final Tab distances = new Tab(this.MSG.distance());
+
+        locations.setPane(this.locationsTable);
+        distances.setPane(this.distancesTable);
+
+        this.tabs.addTab(locations, 0);
+        this.tabs.addTab(distances, 1);
+
+        this.filterResultCount = new StaticTextItem(this.MSG.resultCount());
+        this.filterResultCount.setTitleAlign(Alignment.LEFT);
+        this.filterResultCount.setTitleStyle("color: #000");
+
+        this.filterDescription = new TableFilter();
+        this.filterDescription.setHeight("30px");
+        this.filterDescription.addFilterHandler(this);
+
+        this.filterRange = new RangeTableFilter();
+        this.filterRange.setHeight("30px");
+        this.filterRange.addFilterHandler(this);
+        this.filterRange.setVisible(false);
+
+        final SelectItem filterCriteria = new SelectItem();
+        filterCriteria.setShowTitle(false);
+        filterCriteria.setWidth(100);
+        filterCriteria.addChangedHandler(new ChangedHandler() {
+            @Override
+            public void onChanged(final ChangedEvent e) {
+                if (e.getValue().toString().equals("range")) {
+                    DistancePanelInputHelper.this.filterRange.setVisible(true);
+                    DistancePanelInputHelper.this.filterDescription.setVisible(false);
+                    DistancePanelInputHelper.this.filterDescription.clear();
+                } else {
+                    DistancePanelInputHelper.this.filterRange.setVisible(false);
+                    DistancePanelInputHelper.this.filterRange.clear();
+                    DistancePanelInputHelper.this.filterDescription.setVisible(true);
+                }
+            }
+        });
+
+        final LinkedHashMap<String, String> filterMap = new LinkedHashMap<String, String>();
+        filterMap.put("description", this.MSG.description());
+        filterMap.put("range", this.MSG.range());
+        filterCriteria.setValueMap(filterMap);
+        filterCriteria.setValue("description");
+
+        final DynamicForm form = new DynamicForm();
+        form.setFields(filterCriteria);
+
+        final DynamicForm form2 = new DynamicForm();
+        form2.setFields(this.filterResultCount);
+
+        final HLayout filterLayout = new HLayout();
+        filterLayout.addMember(form);
+        filterLayout.addMember(this.filterDescription);
+        filterLayout.addMember(this.filterRange);
+        filterLayout.setHeight(30);
+        this.tabs.addTabSelectedHandler(new TabSelectedHandler() {
+            @Override
+            public void onTabSelected(final TabSelectedEvent evt) {
+                DistancePanelInputHelper.this.filterDescription.clear();
+                DistancePanelInputHelper.this.filterRange.clear();
+                DistancePanelInputHelper.this.filterResultCount.setValue("");
+
+                final Canvas c = evt.getTabPane();
+                if (c instanceof ListGrid) {
+                    DistancePanelInputHelper.this.currentFiltered = (ListGrid) c;
+                }
+            }
+        });
+
+        this.helperContainer.addMember(this.tabs);
+        this.helperContainer.addMember(filterLayout);
+        this.helperContainer.addMember(form2);
+    }
+
+    @Override
+    public void onFilterCriteriaChanged(final StringFilterEvent event) {
+        final String search = event.getFilter();
+
+        if (search != null && search.length() > 0) {
+            final Criteria c = new Criteria("description", search);
+            this.locationsTable.filterData(c);
+            this.distancesTable.filterData(c);
+            this.filterResultCount.setValue(this.currentFiltered.getRecords().length);
+        } else {
+            this.locationsTable.clearCriteria();
+            this.distancesTable.clearCriteria();
+            this.filterResultCount.setValue("");
+        }
+    }
+
+    @Override
+    public void onFilterCriteriaChanged(final RangeFilterEvent event) {
+        final Float from = event.getFrom() - 0.001f;
+        final Float to = event.getTo() + 0.001f;
+
+        Criterion combinedFilter = null;
+        Criterion locationFilter = null;
+
+        if (from.equals(Float.NaN) && to.equals(Float.NaN)) {
+            this.locationsTable.clearCriteria();
+            this.distancesTable.clearCriteria();
+            this.filterResultCount.setValue("");
+            return;
+        }
+
+        if (from.equals(Float.NaN)) {
+            combinedFilter = new Criterion("to", OperatorId.LESS_OR_EQUAL, to);
+
+            locationFilter = new Criterion("from", OperatorId.LESS_OR_EQUAL, to);
+
+            this.locationsTable.filterData(locationFilter);
+            this.distancesTable.filterData(combinedFilter);
+            this.filterResultCount.setValue(this.currentFiltered.getRecords().length);
+            return;
+        }
+
+        if (to.equals(Float.NaN)) {
+            combinedFilter = new Criterion("from", OperatorId.GREATER_OR_EQUAL, from);
+        } else {
+            final AdvancedCriteria c1 = new AdvancedCriteria(OperatorId.AND,
+                    new Criterion[] { new Criterion("from", OperatorId.GREATER_OR_EQUAL, from), new Criterion("from", OperatorId.LESS_OR_EQUAL, to) });
+
+            final AdvancedCriteria c2 = new AdvancedCriteria(OperatorId.AND,
+                    new Criterion[] { new Criterion("to", OperatorId.GREATER_OR_EQUAL, from), new Criterion("to", OperatorId.LESS_OR_EQUAL, to) });
+
+            final AdvancedCriteria c3 = new AdvancedCriteria(OperatorId.AND,
+                    new Criterion[] { new Criterion("from", OperatorId.LESS_OR_EQUAL, to), new Criterion("to", OperatorId.GREATER_OR_EQUAL, from) });
+
+            combinedFilter = new AdvancedCriteria(OperatorId.OR, new Criterion[] { c1, c2, c3 });
+        }
+        this.locationsTable.filterData(combinedFilter);
+        this.distancesTable.filterData(combinedFilter);
+        this.filterResultCount.setValue(this.currentFiltered.getRecords().length);
+
+    }
+
+    public ListGrid getDistancesTable() {
+        return this.distancesTable;
+    }
+
+    public ListGrid getLocationsTable() {
+        return this.locationsTable;
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/PanelHelper.java	Fri Jul 06 17:56:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/PanelHelper.java	Tue Jul 10 11:24:12 2018 +0200
@@ -9,10 +9,18 @@
  */
 package org.dive4elements.river.client.client.ui;
 
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.types.ListGridFieldType;
 import com.smartgwt.client.widgets.form.fields.IntegerItem;
 import com.smartgwt.client.widgets.form.fields.TextItem;
+import com.smartgwt.client.widgets.form.validator.IntegerRangeValidator;
 import com.smartgwt.client.widgets.form.validator.IsIntegerValidator;
 import com.smartgwt.client.widgets.form.validator.Validator;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.SortNormalizer;
+import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
+import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
 import com.smartgwt.client.widgets.layout.VLayout;
 
 /**
@@ -63,4 +71,48 @@
         return inputItem;
 
     }
+
+    public static final ListGridField createRemoveField(final ListGrid table, final String icon) {
+        final ListGridField removeField = new ListGridField("_removeRecord", "Remove Record") {
+            {
+                setType(ListGridFieldType.ICON);
+                setIcon(icon);
+                setCanEdit(false);
+                setCanFilter(false);
+                setCanSort(false);
+                setCanGroupBy(false);
+                setCanFreeze(false);
+                setWidth(25);
+                setCanDragResize(false);
+                super.setCanToggle(false);
+            }
+        };
+        table.addRecordClickHandler(new RecordClickHandler() {
+            @Override
+            public void onRecordClick(final RecordClickEvent event) {
+                // Just handle remove-clicks
+                if (!event.getField().getName().equals(removeField.getName())) {
+                    return;
+                }
+                event.getViewer().removeData(event.getRecord());
+            }
+        });
+
+        return removeField;
+    }
+
+    public static final ListGridField createIntTableField(final String key, final String msg, final boolean canSort, final SortNormalizer normalizer,
+            final IntegerRangeValidator validators) {
+        final ListGridField intField = new ListGridField(key, msg);
+        intField.setType(ListGridFieldType.INTEGER);
+        intField.setValidators(validators);
+        intField.setWidth(90);
+        intField.setAlign(Alignment.RIGHT);
+        intField.setSortNormalizer(normalizer);
+        intField.setCanSort(canSort);
+        intField.setCanDragResize(false);
+
+        return intField;
+    }
+
 }
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/UIProviderFactory.java	Fri Jul 06 17:56:18 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/UIProviderFactory.java	Tue Jul 10 11:24:12 2018 +0200
@@ -32,6 +32,7 @@
 import org.dive4elements.river.client.client.ui.uinfo.LoadSedimentHeightPanel;
 import org.dive4elements.river.client.client.ui.uinfo.LoadSingleEpochSelect;
 import org.dive4elements.river.client.client.ui.uinfo.LoadSingleYearPanel;
+import org.dive4elements.river.client.client.ui.uinfo.SupraRegionalTablePanel;
 import org.dive4elements.river.client.client.ui.uinfo.VegetationzonesTableEditPanel;
 import org.dive4elements.river.client.client.ui.uinfo.VegetationzonesTablePanel;
 import org.dive4elements.river.client.shared.model.User;
@@ -179,6 +180,8 @@
             return new UserRGDProvider();
         } else if (uiProvider.equals("static_sqrelation")) {
             return new StaticDataPanel();
+        } else if (uiProvider.equals("uinfo.salix.supraregional.table")) {
+            return new SupraRegionalTablePanel();
         }
 
         if ("sinfo_flowdepth_twin_panel".equals(uiProvider))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/uinfo/SupraRegionalTablePanel.java	Tue Jul 10 11:24:12 2018 +0200
@@ -0,0 +1,412 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.client.client.ui.uinfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dive4elements.river.client.client.ui.AbstractUIProvider;
+import org.dive4elements.river.client.client.ui.DistancePanelInputHelper;
+import org.dive4elements.river.client.client.ui.PanelHelper;
+import org.dive4elements.river.client.shared.model.Data;
+import org.dive4elements.river.client.shared.model.DataItem;
+import org.dive4elements.river.client.shared.model.DataList;
+import org.dive4elements.river.client.shared.model.DefaultData;
+import org.dive4elements.river.client.shared.model.DefaultDataItem;
+import org.dive4elements.river.client.shared.model.SalixZone;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.user.client.ui.Label;
+import com.smartgwt.client.data.Record;
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Button;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.TextItem;
+import com.smartgwt.client.widgets.grid.CellEditValueFormatter;
+import com.smartgwt.client.widgets.grid.CellFormatter;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.events.CellSavedEvent;
+import com.smartgwt.client.widgets.grid.events.CellSavedHandler;
+import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
+import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.Layout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+public class SupraRegionalTablePanel extends AbstractUIProvider {
+    private static final long serialVersionUID = 1L;
+
+    private Double minKm, maxKm = -1.;
+    // private String tableData = "";
+    private ListGrid table;
+    private final String datakey = "supraregional_table";
+    final Label validationLabel = new Label();
+
+    private final NumberFormat format = NumberFormat.getDecimalFormat();
+
+    @Override
+    public Canvas createOld(final DataList dataList) {
+        final HLayout layout = new HLayout();
+        layout.setWidth("400px");
+        final VLayout vLayout = new VLayout();
+        vLayout.setWidth(130);
+        final Label label = new Label(dataList.getLabel());
+        label.setWidth("200px");
+        label.setHeight("25px");
+
+        final List<Data> items = dataList.getAll();
+        final Data str = getData(items, this.datakey);
+        final DataItem[] strItems = str.getItems();
+
+        final List<SalixZone> entries = SalixZone.parse(strItems[0].getLabel());
+
+        for (final SalixZone entry : entries) {
+            final Label dateLabel = new Label(
+                    entry.getDwsplValue() + " (" + this.format.format(entry.getFromKm()) + "-" + this.format.format(entry.getToKm()) + " km)");
+            dateLabel.setHeight("20px");
+            vLayout.addMember(dateLabel);
+        }
+        final Canvas back = getBackButton(dataList.getState());
+        layout.addMember(label);
+        layout.addMember(vLayout);
+        layout.addMember(back);
+
+        return layout;
+    }
+
+    @Override
+    public Canvas create(final DataList data) {
+
+        final Data dataItem = data.getAll().get(0);
+        String tableData = "";
+        for (final DataItem item : dataItem.getItems()) {
+            final String label = item.getLabel();
+            if (label.equals("salix_zones_min"))
+                this.minKm = Double.valueOf(item.getStringValue());
+            else if (label.equals("salix_zones_max"))
+                this.maxKm = Double.valueOf(item.getStringValue());
+            else if (label.equals("salix_zones"))
+                tableData = item.getStringValue();
+        }
+
+        if (this.minKm > this.maxKm) {
+            final double temp = this.minKm;
+            this.minKm = this.maxKm;
+            this.maxKm = temp;
+        }
+
+        final VLayout layout = new VLayout();
+        final Canvas submit = getNextButton();
+
+        final VLayout root = new VLayout();
+        root.setWidth(420);
+        createWidget(root, data, tableData);
+
+        layout.addMember(root);
+        layout.addMember(submit);
+
+        validateRangeOverlap();// init Text
+        return layout;
+    }
+
+    @Override
+    protected Data[] getData() {
+        final List<Data> data = new ArrayList<Data>();
+
+        final ListGridRecord[] lgr = this.table.getRecords();
+        if (lgr == null) {
+            return new Data[0];
+        }
+        final List<SalixZone> zoneList = getSalixZones(lgr);
+        final DataItem item = new DefaultDataItem(this.datakey, null, SalixZone.parseListToDataString(zoneList));
+        data.add(new DefaultData(this.datakey, null, null, new DataItem[] { item }));
+        return data.toArray(new Data[data.size()]);
+    }
+
+    private List<SalixZone> getSalixZones(final ListGridRecord[] lgr) {
+        final List<SalixZone> zoneList = new ArrayList<SalixZone>();
+        for (final ListGridRecord element : lgr) {
+            final Record r = element;
+            final SalixZone zone = SalixZone.createFromTableEntry(r.getAttribute("dmwspl"), r.getAttribute("from"), r.getAttribute("to"));
+            zoneList.add(zone);
+        }
+        return zoneList;
+    }
+
+    public Canvas createWidget(final Layout root, final DataList data, final String tableData) {
+
+        this.table = createTable(root, data, tableData);
+
+        final TextItem dwspl = PanelHelper.createItem("_label_dwspl", this.MSG.uinfo_salix_dmwspl_short(), "50");
+        // this.dwspl.setColSpan(3);
+        final TextItem start = PanelHelper.createIntegerItem("_from_dwspl", getLabelFromTo(this.MSG.from()), "50");
+        // this.start.setColSpan(3);
+        final TextItem end = PanelHelper.createIntegerItem("_to_dwspl", getLabelFromTo(this.MSG.to()), "50");
+        // this.end.setColSpan(3);
+
+        final HLayout fields = new HLayout();
+
+        final Button add = new Button(this.MSG.add_date()); // TODO: make key more generic or change to more specific
+
+        final DynamicForm newEntryForm = new DynamicForm();
+        newEntryForm.setWidth(320);
+        newEntryForm.setNumCols(12);
+        newEntryForm.setFields(dwspl, start, end);
+
+        add.addClickHandler(new ClickHandler() {
+            @Override
+            public void onClick(final ClickEvent ce) {
+                final String v1 = start.getValueAsString();
+                final String v2 = end.getValueAsString();
+                final String v3 = dwspl.getValueAsString();
+                final String message = validate(v1, v2, v3);
+                if (message != null) {
+                    SC.warn(message);
+                    return;
+                }
+                final ListGridRecord r = new ListGridRecord();
+                r.setAttribute("dmwspl", v3);
+                r.setAttribute("from", SupraRegionalTablePanel.this.format.parse(v1));
+                r.setAttribute("to", SupraRegionalTablePanel.this.format.parse(v2));
+                SupraRegionalTablePanel.this.table.addData(r);
+                validateRangeOverlap();
+            }
+        });
+
+        fields.addMember(newEntryForm);
+
+        root.addMember(fields);
+        root.addMember(PanelHelper.getSpacer(10));
+
+        initHelperPanel(start, end);
+
+        root.addMember(add);
+        root.addMember(PanelHelper.getSpacer(20));
+        return root;
+    }
+
+    private String getLabelFromTo(final String fromOrTo) {
+        return new StringBuilder().append(fromOrTo).append(" [").append(this.MSG.dpUnitFrom()).append("]").toString();
+    }
+
+    protected void initHelperPanel(final TextItem start, final TextItem end) {
+        final DistancePanelInputHelper helper = new DistancePanelInputHelper(this.MSG, this.helperContainer, this.getRiverName());
+
+        helper.getDistancesTable().addRecordClickHandler(new RecordClickHandler() {
+            @Override
+            public void onRecordClick(final RecordClickEvent e) {
+                final Record r = e.getRecord();
+
+                final String from = r.getAttribute("from");
+                final String to = r.getAttribute("to");
+
+                try {
+                    start.setValue(SupraRegionalTablePanel.this.format.format(Double.valueOf(from)));
+                    end.setValue(SupraRegionalTablePanel.this.format.format(Double.valueOf(to)));
+                }
+                catch (final NumberFormatException nfe) {
+                    SC.warn(SupraRegionalTablePanel.this.MSG.wrongFormat());
+                }
+            }
+        });
+
+        helper.getLocationsTable().addRecordClickHandler(new RecordClickHandler() {
+            @Override
+            public void onRecordClick(final RecordClickEvent e) {
+                final Record r = e.getRecord();
+                final int field = e.getFieldNum();
+
+                try {
+                    final String value = r.getAttribute("from");
+                    switch (field) {
+                    case 0:
+                        start.setValue(SupraRegionalTablePanel.this.format.format(Double.valueOf(value)));
+                        break;
+                    case 1:
+                        end.setValue(SupraRegionalTablePanel.this.format.format(Double.valueOf(value)));
+                        break;
+                    }
+                }
+                catch (final NumberFormatException nfe) {
+                    SC.warn(SupraRegionalTablePanel.this.MSG.wrongFormat());
+                }
+            }
+        });
+
+    }
+
+    protected final ListGrid createTable(final Layout root, final DataList data, final String tableData) {
+
+        final Label title = new Label(data.get(0).getDescription());
+        title.setHeight("35px");
+        this.validationLabel.setHeight("10px");
+
+        final ListGrid elements = new ListGrid();
+        elements.setWidth(320);
+        elements.setHeight(300);
+        elements.setShowHeaderContextMenu(false);
+        elements.setCanReorderFields(false);
+        elements.setCanResizeFields(false);
+        elements.setCanEdit(true);
+        elements.setCanSort(false);
+        elements.setCanResizeFields(false);
+
+        final ListGridField dmwsplField = new ListGridField("dmwspl", this.MSG.uinfo_salix_dmwspl_short());
+        dmwsplField.setAlign(Alignment.RIGHT);
+        dmwsplField.setWidth(95);
+        dmwsplField.addCellSavedHandler(new CellSavedHandler() {
+            @Override
+            public void onCellSaved(final CellSavedEvent event) {
+                final Object value = event.getNewValue();
+                event.getRecord().setAttribute("dmwspl", event.getOldValue());
+                try {
+                    final int intValue = Integer.valueOf(String.valueOf(value));
+                    event.getRecord().setAttribute("dmwspl", intValue);
+                }
+                catch (final NumberFormatException e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+        final ListGridField fromField = createDoubleTableField("from", this.MSG.from());
+        final ListGridField toField = createDoubleTableField("to", this.MSG.to());
+        final ListGridField removeField = PanelHelper.createRemoveField(elements, GWT.getHostPageBaseURL() + this.MSG.removeFeature());
+        elements.addRecordClickHandler(new RecordClickHandler() { // adding another custom record-Remove-Handler which is not included in the Panelhelper
+            @Override
+            public void onRecordClick(final RecordClickEvent event) {
+                if (event.getField().getName().equals(removeField.getName())) {
+                    validateRangeOverlap();
+                }
+            }
+        });
+        elements.setFields(dmwsplField, fromField, toField, removeField);
+        addDataInit(elements, tableData);
+
+        root.addMember(title);
+        root.addMember(elements);
+        root.addMember(PanelHelper.getSpacer(3));
+        root.addMember(this.validationLabel);
+        root.addMember(PanelHelper.getSpacer(4));
+        return elements;
+    }
+
+    private void addDataInit(final ListGrid table, final String tableData) {
+        if (tableData != null) {
+            final List<SalixZone> rows = SalixZone.parse(tableData);
+            for (final SalixZone row : rows) {
+                table.addData(createEntry(row));
+            }
+        }
+    }
+
+    public final ListGridRecord createEntry(final SalixZone row) {
+
+        final Integer dwspl = row.getDwsplValue();
+        final Double from = row.getFromKm();
+        final Double to = row.getToKm();
+
+        if (dwspl == null || from == null || to == null) {
+            return null;
+        }
+
+        final ListGridRecord r = new ListGridRecord();
+        r.setAttribute("dmwspl", dwspl);
+        r.setAttribute("from", from);
+        r.setAttribute("to", to);
+
+        return r;
+
+    }
+
+    private ListGridField createDoubleTableField(final String key, final String msg) {
+        final ListGridField field = new ListGridField(key, msg);
+        field.setAlign(Alignment.RIGHT);
+        field.setWidth(90);
+        field.addCellSavedHandler(new CellSavedHandler() {
+            @Override
+            public void onCellSaved(final CellSavedEvent event) {
+                final Object value = event.getNewValue();
+                event.getRecord().setAttribute(key, event.getOldValue());
+                try {
+                    final double d = SupraRegionalTablePanel.this.format.parse(String.valueOf(value));
+                    if (d > SupraRegionalTablePanel.this.maxKm) {
+                        event.getRecord().setAttribute(key, SupraRegionalTablePanel.this.maxKm);
+                    } else if (d < SupraRegionalTablePanel.this.minKm) {
+                        event.getRecord().setAttribute(key, SupraRegionalTablePanel.this.minKm);
+                    } else {
+                        event.getRecord().setAttribute(key, d);
+                    }
+                    validateRangeOverlap();
+                }
+                catch (final NumberFormatException e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+
+        field.setEditValueFormatter(new CellEditValueFormatter() {
+            @Override
+            public Object format(final Object value, final ListGridRecord record, final int rowNum, final int colNum) {
+                if (!(value instanceof Float || value instanceof Double)) {
+                    try {
+                        return (float) SupraRegionalTablePanel.this.format.parse(String.valueOf(value));
+                    }
+                    catch (final Exception e2) {
+                        return record.getAttribute(key);
+                    }
+                } else {
+                    return SupraRegionalTablePanel.this.format.format((Double) value);
+                }
+            }
+        });
+        field.setCellFormatter(new CellFormatter() {
+            @Override
+            public String format(final Object value, final ListGridRecord record, final int rowNum, final int colNum) {
+                return SupraRegionalTablePanel.this.format.format(Double.valueOf(String.valueOf(value)));
+            }
+        });
+        return field;
+    }
+
+    private String validate(final String fromInput, final String toInput, final String dmwspl) {
+
+        if (fromInput == null || toInput == null || dmwspl == null || fromInput.trim().isEmpty() || toInput.trim().isEmpty() || dmwspl.trim().isEmpty())
+            return this.MSG.uinfo_vegetation_zones_validation_empty();
+
+        try {
+            final Double from = this.format.parse(fromInput);
+            final Double to = this.format.parse(toInput);
+            Integer.valueOf(dmwspl);
+
+            if (from < this.minKm || from > this.maxKm || to > this.maxKm || to < this.minKm)
+                return this.MSG.uinfo_salix_km_limit_exceed();
+            return null;
+        }
+        catch (final NumberFormatException e) {
+            return this.MSG.wrongFormat();
+        }
+    }
+
+    private void validateRangeOverlap() {
+        final StringBuilder builder = new StringBuilder();
+        if (SalixZone.zonesAreOverlapping(this.getSalixZones(this.table.getRecords())))
+            builder.append(this.MSG.uinfo_salix_km_overlap()).append(" ");
+        if (SalixZone.hasGaps(this.getSalixZones(this.table.getRecords()), this.minKm, this.maxKm))
+            builder.append(this.MSG.uinfo_salix_km_has_gaps());// "Der Km-Bereich wird nicht vollst. abgedeckt.";
+
+        this.validationLabel.setText(builder.toString());
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/SalixZone.java	Tue Jul 10 11:24:12 2018 +0200
@@ -0,0 +1,155 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.client.shared.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * @author Domenico Nardi Tironi
+ *
+ */
+public class SalixZone implements Comparable<SalixZone> {
+
+    // IMMER ABGLEICHEN Server Client SalixZone.java
+    private static final String TABLE_CELL_SEPARATOR = "TABLE_CELL_SEPARATOR";
+    private static final String TABLE_ROW_SEPARATOR = "TABLE_ROW_SEPARATOR";
+    private final int dwsplValue;
+    private final double fromKm;
+    private final double toKm;
+
+    private final static double DELTA = 0.0001;
+
+    public static List<SalixZone> parse(final String zonesRaw) {
+        final List<SalixZone> resultList = new ArrayList<SalixZone>();
+
+        final List<String[]> results = new ArrayList<String[]>();
+        if (zonesRaw.contains(TABLE_ROW_SEPARATOR)) {
+            final String[] rows = zonesRaw.split(TABLE_ROW_SEPARATOR);
+            for (final String row : rows) {
+                if (row.contains(TABLE_CELL_SEPARATOR)) {
+                    final String[] result = row.split(TABLE_CELL_SEPARATOR);
+                    results.add(result);
+                }
+            }
+        }
+        for (final String[] zone : results) {
+            final SalixZone helper = new SalixZone(Integer.valueOf(zone[0]), Double.valueOf(zone[1]), Double.valueOf(zone[2]));
+            resultList.add(helper);
+        }
+        return resultList;
+    }
+
+    public static SalixZone createFromTableEntry(final String dwspl, final String from, final String to) {
+        return new SalixZone(Integer.valueOf(dwspl), Double.valueOf(from), Double.valueOf(to)); // Error-Handling?
+    }
+
+    private SalixZone(final int dwsplValue, final double fromKm, final double toKm) {
+        this.dwsplValue = dwsplValue;
+        this.fromKm = fromKm;
+        this.toKm = toKm;
+    }
+
+    public Double getToKm() {
+        return this.toKm;
+    }
+
+    public int getDwsplValue() {
+        return this.dwsplValue;
+    }
+
+    public Double getFromKm() {
+        return this.fromKm;
+    }
+
+    public static final String parseListToDataString(final List<SalixZone> list) {
+
+        java.util.Collections.sort(list);
+        final StringBuilder builder = new StringBuilder();
+        for (final SalixZone zone : list) {
+            builder.append(zone.getDwsplValue());
+            builder.append(TABLE_CELL_SEPARATOR);
+            builder.append(zone.getFromKm());
+            builder.append(TABLE_CELL_SEPARATOR);
+            builder.append(zone.getToKm());
+            builder.append(TABLE_ROW_SEPARATOR);
+        }
+        return builder.toString();
+    }
+
+    public static final boolean zonesAreOverlapping(final List<SalixZone> list) {
+        for (final SalixZone zone : list) {
+            for (final SalixZone zoneOther : list) {
+                if (zone != zoneOther) {
+                    final boolean overlaps = zone.overlaps(zoneOther);
+                    if (overlaps) {
+                        return overlaps; // cancel. only one zone has to overlap
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public static final boolean hasGaps(final List<SalixZone> list, final double lower, final double upper) {
+
+        if (((upper - lower) > DELTA) && list.size() == 0)
+            return true;
+
+        final TreeSet<SalixZone> treeList = new TreeSet<SalixZone>();
+        treeList.addAll(list);
+        double lowerCompare = lower + DELTA;
+        for (final SalixZone zone : treeList) {
+            if (zone.getLowerFromTo() > lowerCompare) {
+                return true;
+            }
+            lowerCompare = zone.getUpperFromTo() + DELTA;
+        }
+        if ((lowerCompare + DELTA) < upper)
+            return true; // am Ende nicht geschlossen
+
+        return false;
+    }
+
+    private Double getLowerFromTo() {
+        return this.fromKm < this.toKm ? this.fromKm : this.toKm; // Math. is forbidden :-(
+    }
+
+    private Double getUpperFromTo() {
+        return this.fromKm > this.toKm ? this.fromKm : this.toKm;// Math. is forbidden :-(
+    }
+
+    private boolean overlaps(final SalixZone otherZone) {
+        final double otherLower = otherZone.getLowerFromTo();
+        final double otherUpper = otherZone.getUpperFromTo();
+
+        final double upper = getUpperFromTo() - DELTA;
+        final double lower = getLowerFromTo() + DELTA;
+        final double otherSchwerpunkt = (otherLower + otherUpper) / 2;
+        if ((otherUpper < upper && otherUpper > lower)) {
+            return true;
+        } else if (otherLower > lower && otherLower < upper) {
+            return true;
+        } else if (otherSchwerpunkt > lower && otherSchwerpunkt < upper) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int compareTo(final SalixZone o) {
+        final int basicCompare = this.getLowerFromTo().compareTo(o.getLowerFromTo());
+        return basicCompare;
+    }
+
+}

http://dive4elements.wald.intevation.org