changeset 9415:9744ce3c3853

Rework of fixanalysis computation and dWt and WQ facets. Got rid of strange remapping and bitshifting code by explicitely saving the column information and using it in the facets. The facets also put the valid station range into their xml-metadata
author gernotbelger
date Thu, 16 Aug 2018 16:27:53 +0200
parents 096f151a0a9f
children 05405292a7ca
files artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsColumn.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsColumnFactory.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsFilterBuilder.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverviewFactory.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/OfficialLineFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/QW.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/Range.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/RangeWithValues.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/AnalysisPeriod.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/AnalysisPeriodEventResults.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/ColumnCache.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/ColumnQRangeLoader.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/DateUniqueMaker.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/Fitting.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisEventsFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisPeriodsFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixColumnLoader.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalAnalysisFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalReferenceFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculationExtended.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixReferenceEventsFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResultColumn.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResultColumns.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/Fixing.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingColumn.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingColumnData.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingColumnFilter.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingColumnWithData.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsColumnFactory.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsEventFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsFilterBuilder.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsOverview.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsOverviewFactory.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/KMRangeLoader.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QRange.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWD.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWI.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/RankRemapper.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/SectorRange.java artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsKMChartService.java artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsOverviewService.java artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/EventSelect.java artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixAnalysisCompute.java artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixRealizingCompute.java artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/LocationSelect.java artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/UniqueDateFormatter.java artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java artifacts/src/main/java/org/dive4elements/river/utils/KMIndex.java artifacts/src/main/java/org/dive4elements/river/utils/UniqueDateFormatter.java
diffstat 58 files changed, 2781 insertions(+), 2977 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsColumn.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.artifacts.model;
-
-import org.dive4elements.river.artifacts.math.Linear;
-
-import java.util.Arrays;
-
-import java.io.Serializable;
-
-public class FixingsColumn
-implements   Serializable
-{
-    protected double [] kms;
-    protected double [] ws;
-
-    protected QRangeTree qs;
-
-    public FixingsColumn() {
-    }
-
-    public FixingsColumn(
-        double []  kms,
-        double []  ws,
-        QRangeTree qs
-    ) {
-        this.kms = kms;
-        this.ws  = ws;
-        this.qs  = qs;
-    }
-
-    public boolean getW(double km, double [] w) {
-        return getW(km, w, 0);
-    }
-
-    public boolean getW(double km, double [] w, int index) {
-
-        if (kms.length == 0 || km < kms[0] || km > kms[kms.length-1]) {
-            w[index] = Double.NaN;
-            return true;
-        }
-
-        int idx = Arrays.binarySearch(kms, km);
-
-        if (idx >= 0) {
-            w[index] = ws[idx];
-            return true;
-        }
-
-        idx = -idx - 1;
-
-        w[index] = Linear.linear(km, kms[idx-1], kms[idx], ws[idx-1], ws[idx]);
-        return false;
-    }
-
-    public double getQ(double km) {
-        return qs.findQ(km);
-    }
-
-    public QRangeTree getQRanges() {
-        return qs;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsColumnFactory.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.artifacts.model;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
-
-import org.dive4elements.river.artifacts.cache.CacheFactory;
-
-import org.dive4elements.river.backend.SessionHolder;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.hibernate.Session;
-import org.hibernate.SQLQuery;
-
-import org.hibernate.type.StandardBasicTypes;
-
-import org.apache.log4j.Logger;
-
-public class FixingsColumnFactory
-{
-    private static Logger log = Logger.getLogger(FixingsColumnFactory.class);
-
-    public static final String CACHE_NAME = "fixings-columns";
-
-    public static final String SQL_COLUMN_WS =
-        "SELECT wcv.position AS km, wcv.w AS w " +
-        "FROM wst_column_values wcv " +
-        "WHERE wst_column_id = :column_id " +
-        "ORDER by wcv.position";
-
-    public static final String SQL_COLUMN_QS =
-        "SELECT wqr.q AS q, r.a AS a, r.b AS b " +
-        "FROM wst_column_q_ranges wcqr " +
-        "JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id = wqr.id " +
-        "JOIN ranges r         ON wqr.range_id        = r.id " +
-        "WHERE wcqr.wst_column_id = :column_id ORDER by r.a";
-
-    public static final FixingsColumnFactory INSTANCE =
-        new FixingsColumnFactory();
-
-    private FixingsColumnFactory() {
-    }
-
-    public static FixingsColumnFactory getInstance() {
-        return INSTANCE;
-    }
-
-    public FixingsColumn getColumnData(Fixing.Column column) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug("FixingsColumnFactory.getColumnData");
-        }
-
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            if (debug) {
-                log.debug("Cache unconfigured.");
-            }
-            return getUncached(column);
-        }
-
-        Integer cacheKey = Integer.valueOf(column.getId());
-        Element element  = cache.get(cacheKey);
-
-        if (element != null) {
-            if (debug) {
-                log.debug("Column " + cacheKey + " found in cache.");
-            }
-            return (FixingsColumn)element.getValue();
-        }
-        else {
-            FixingsColumn result = getUncached(column);
-            if (result != null) {
-                if (debug) {
-                    log.debug("Store column " + cacheKey + " into cache.");
-                }
-                cache.put(new Element(cacheKey, result));
-            }
-            return result;
-        }
-    }
-
-    protected FixingsColumn getUncached(Fixing.Column column) {
-        Session session = SessionHolder.HOLDER.get();
-
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_COLUMN_WS)
-            .addScalar("km", StandardBasicTypes.DOUBLE)
-            .addScalar("w",  StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("column_id", column.getId());
-
-        List<Object []> results = sqlQuery.list();
-
-        if (results.isEmpty()) {
-            return null;
-        }
-
-        double [] kms = new double[results.size()];
-        double [] ws  = new double[kms.length];
-
-        for (int i = 0; i < kms.length; ++i) {
-            Object [] row = results.get(i);
-            kms[i] = ((Double)row[0]).doubleValue();
-            ws [i] = ((Double)row[1]).doubleValue();
-        }
-
-        sqlQuery = session.createSQLQuery(SQL_COLUMN_QS)
-            .addScalar("q", StandardBasicTypes.DOUBLE)
-            .addScalar("a", StandardBasicTypes.DOUBLE)
-            .addScalar("b", StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("column_id", column.getId());
-
-        results = sqlQuery.list();
-
-        if (results.isEmpty()) {
-            return null;
-        }
-
-        QRangeTree qs = new QRangeTree(
-            results, QRangeTree.WITHOUT_COLUMN, 0, results.size());
-
-        return new FixingsColumn(kms, ws, qs);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsFilterBuilder.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.artifacts.model;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview.AndFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.DateFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.DateRangeFilter;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview.IdFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.IdsFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.KmFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.NotFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.OrFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.SectorFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.SectorRangeFilter;
-
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-public class FixingsFilterBuilder
-{
-    private static Logger log = Logger.getLogger(FixingsFilterBuilder.class);
-
-    protected Filter   filter;
-    protected Range    range;
-
-    protected Document document;
-
-    public FixingsFilterBuilder() {
-    }
-
-    public FixingsFilterBuilder(Document document) {
-        this.document = document;
-    }
-
-    public Filter getFilter() {
-        if (filter == null) {
-            filter = buildFilter();
-        }
-        return filter;
-    }
-
-    public Range getRange() {
-        if (range == null) {
-            range = buildRange();
-        }
-        return range;
-    }
-
-    public Document getDocument() {
-        return document;
-    }
-
-    protected Range buildRange() {
-
-        NodeList ranges = document.getElementsByTagName("range");
-
-        if (ranges.getLength() < 1) {
-            return FixingsOverview.FULL_EXTENT;
-        }
-
-        Element range = (Element)ranges.item(0);
-
-        String from = range.getAttribute("from").trim();
-        String to   = range.getAttribute("to"  ).trim();
-
-        double start = -Double.MAX_VALUE;
-        double end   =  Double.MAX_VALUE;
-
-        if (from.length() > 0) {
-            try {
-                start = Double.parseDouble(from);
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("Invalid from value: " + from);
-            }
-        }
-
-        if (to.length() > 0) {
-            try {
-                end = Double.parseDouble(to);
-            }
-            catch (NumberFormatException nfe) {
-                log.warn("Invalid to value: " + to);
-            }
-        }
-
-        if (start > end) {
-            double t = start;
-            start = end;
-            end = t;
-        }
-
-        return new Range(start, end);
-    }
-
-    protected Filter buildFilter() {
-        NodeList filters = document.getElementsByTagName("filter");
-
-        return filters.getLength() < 1
-            ? FixingsOverview.ACCEPT
-            : buildFilter((Element)filters.item(0));
-    }
-
-    protected static Filter buildFilter(Element root) {
-        List<Filter> filters = buildRecursiveFilter(root);
-        switch (filters.size()) {
-            case  0: return FixingsOverview.ACCEPT;
-            case  1: return filters.get(0);
-            default: return new AndFilter(filters);
-        }
-    }
-
-    protected static final Date parseDate(String text) {
-        SimpleDateFormat format =
-            new SimpleDateFormat(FixingsOverview.DATE_FORMAT);
-        return format.parse(text, new ParsePosition(0));
-    }
-
-    protected static List<Filter> buildRecursiveFilter(Element root) {
-        List<Filter> filters = new ArrayList<Filter>();
-
-        NodeList children = root.getChildNodes();
-
-        for (int i = 0, N = children.getLength(); i < N; ++i) {
-            Node child = children.item(i);
-            if (child.getNodeType() != Node.ELEMENT_NODE) {
-                continue;
-            }
-
-            Element element = (Element)child;
-            String name = element.getLocalName();
-
-            if ("and".equals(name)) {
-                filters.add(new AndFilter(buildRecursiveFilter(element)));
-            }
-            else if ("or".equals(name)) {
-                filters.add(new OrFilter(buildRecursiveFilter(element)));
-            }
-            else if ("not".equals(name)) {
-                List<Filter> childrenFilters = buildRecursiveFilter(element);
-                if (!childrenFilters.isEmpty()) {
-                    filters.add(new NotFilter(childrenFilters.get(0)));
-                }
-            }
-            else if ("column".equals(name)) {
-                String cid = element.getAttribute("cid").trim();
-                if (cid.length() > 0) {
-                    try {
-                        filters.add(new IdFilter(Integer.parseInt(cid)));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-            }
-            else if ("columns".equals(name)) {
-                String cidsS = element.getAttribute("cids").trim();
-                String [] parts = cidsS.split("\\s+");
-                List<Integer> ids = new ArrayList<Integer>();
-                for (String part: parts) {
-                    try {
-                        ids.add(Integer.valueOf(part));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-                int [] cids = new int[ids.size()];
-                for (int j = 0; j < cids.length; ++j) {
-                    cids[j] = ids.get(j);
-                }
-                filters.add(new IdsFilter(cids));
-            }
-            else if ("date".equals(name)) {
-                String when = element.getAttribute("when").trim();
-                if (when.length() > 0) {
-                    Date date = parseDate(when);
-                    if (date != null) {
-                        filters.add(new DateFilter(date));
-                    }
-                }
-            }
-            else if ("date-range".equals(name)) {
-                String from = element.getAttribute("from").trim();
-                String to   = element.getAttribute("to"  ).trim();
-                if (from.length() > 0 && to.length() > 0) {
-                    Date start = parseDate(from);
-                    Date end   = parseDate(to);
-                    if (start != null && end != null) {
-                        filters.add(new DateRangeFilter(start, end));
-                    }
-                }
-            }
-            else if ("sector-range".equals(name)) {
-                String from = element.getAttribute("from").trim();
-                String to   = element.getAttribute("to"  ).trim();
-                if (from.length() > 0 && to.length() > 0) {
-                    try {
-                        filters.add(new SectorRangeFilter(
-                            Integer.parseInt(from),
-                            Integer.parseInt(to)));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-            }
-            else if ("sector".equals(name)) {
-                String value = element.getAttribute("value").trim();
-                if (value.length() > 0) {
-                    try {
-                        filters.add(new SectorFilter(Integer.parseInt(value)));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-            }
-            else if ("position".equals(name)) {
-                String km = element.getAttribute("km").trim();
-                if (km.length() > 0) {
-                    try {
-                        filters.add(new KmFilter(Double.parseDouble(km)));
-                    }
-                    catch (NumberFormatException nfe) {
-                        log.warn(nfe);
-                    }
-                }
-            }
-        }
-
-        return filters;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,973 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.artifacts.model;
-
-import java.io.Serializable;
-
-import java.text.SimpleDateFormat;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.SQLQuery;
-import org.hibernate.Session;
-
-import org.hibernate.type.StandardBasicTypes;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import org.dive4elements.river.utils.BatchLoader;
-
-
-/** Generate Fixings Table overview data structure to be stored in cache. */
-public class FixingsOverview
-implements   Serializable
-{
-    private static Logger log = Logger.getLogger(FixingsOverview.class);
-
-    public static final double EPSILON = 1e-2;
-
-    public static final String DATE_FORMAT = "dd.MM.yyyy";
-
-    public static final String SQL_RIVER_ID =
-        "SELECT" +
-        "    id AS river_id," +
-        "    km_up " +
-        "FROM rivers " +
-        "WHERE" +
-        "    name = :name";
-
-    /** All kind-2 wsts from given river. */
-    public static final String SQL_FIXINGS =
-        "SELECT" +
-        "    id AS wst_id," +
-        "    description " +
-        "FROM wsts " +
-        "WHERE" +
-        "    river_id = :river_id AND kind = 2";
-
-    public static final String SQL_FIXING_COLUMNS_BATCH =
-        "SELECT " +
-            "wc.wst_id     AS wst_id," +
-            "wc.id         AS wst_column_id," +
-            "ti.start_time AS start_time," +
-            "wc.name       AS name " +
-        "FROM wst_columns wc " +
-            "JOIN time_intervals ti ON wc.time_interval_id = ti.id " +
-        "WHERE " +
-            "wc.wst_id IN ($IDS) " +
-        "ORDER BY wc.wst_id, position";
-
-    public static final String SQL_FIXING_COLUMN_Q_RANGES_BATCH =
-        "SELECT " +
-            "wcqr.wst_column_id AS wst_column_id," +
-            "wqr.q              AS q," +
-            "r.a                AS start_km," +
-            "r.b                AS stop_km " +
-        "FROM wst_column_q_ranges wcqr " +
-            "JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id = wqr.id " +
-            "JOIN ranges       r   ON wqr.range_id        = r.id " +
-        "WHERE " +
-            "wcqr.wst_column_id IN ($IDS) " +
-        "ORDER BY wcqr.wst_column_id, r.a";
-
-    public static final String SQL_FIXING_COLUMN_KM_RANGE_BATCH =
-        "SELECT " +
-            "wst_column_id," +
-            "MIN(position) AS start_km," +
-            "MAX(position) AS stop_km " +
-        "FROM " +
-            "wst_column_values " +
-        "WHERE " +
-            "wst_column_id IN ($IDS) " +
-        "GROUP BY wst_column_id";
-
-    public static final class KMRangeLoader extends BatchLoader<double []> {
-
-        public KMRangeLoader(List<Integer> columns, Session session) {
-            super(columns, session, SQL_FIXING_COLUMN_KM_RANGE_BATCH);
-        }
-
-        @Override
-        protected void fill(SQLQuery query) {
-            query
-                .addScalar("wst_column_id", StandardBasicTypes.INTEGER)
-                .addScalar("start_km",      StandardBasicTypes.DOUBLE)
-                .addScalar("stop_km",       StandardBasicTypes.DOUBLE);
-
-            List<Object []> ranges = query.list();
-            for (Object [] r: ranges) {
-                Integer cid = (Integer)r[0];
-                double [] vs = new double [] { (Double)r[1], (Double)r[2] };
-                cache(cid, vs);
-            }
-        }
-    } // class KMRangeLoader
-
-    public static final class ColumnQRangeLoader
-    extends                   BatchLoader<List<double []>>
-    {
-        public ColumnQRangeLoader(List<Integer> columns, Session session) {
-            super(columns, session, SQL_FIXING_COLUMN_Q_RANGES_BATCH);
-        }
-
-        @Override
-        protected void fill(SQLQuery query) {
-            query
-                .addScalar("wst_column_id", StandardBasicTypes.INTEGER)
-                .addScalar("q",             StandardBasicTypes.DOUBLE)
-                .addScalar("start_km",      StandardBasicTypes.DOUBLE)
-                .addScalar("stop_km",       StandardBasicTypes.DOUBLE);
-
-            int lastId = Integer.MIN_VALUE;
-            List<double []> column = new ArrayList<double []>();
-
-            List<Object []> ranges = query.list();
-            for (Object [] r: ranges) {
-                int cid = (Integer)r[0];
-
-                if (cid != lastId && !column.isEmpty()) {
-                    cache(lastId, column);
-                    column = new ArrayList<double []>();
-                }
-                column.add(new double [] {
-                    (Double)r[1],
-                    (Double)r[2],
-                    (Double)r[3]
-                });
-
-                lastId = cid;
-            }
-
-            if (!column.isEmpty()) {
-                cache(lastId, column);
-            }
-        }
-    } // class ColumnQRangeLoader
-
-    /** Helper class to store data from batching fixing columns. */
-    private static final class FixColumn {
-        int    columnId;
-        Date   startTime;
-        String name;
-
-        FixColumn(int columnId, Date startTime, String name) {
-            this.columnId  = columnId;
-            this.startTime = startTime;
-            this.name      = name;
-        }
-    } // class FixColumn
-
-    public static final class FixColumnLoader
-    extends                   BatchLoader<List<FixColumn>>
-    {
-        public FixColumnLoader(List<Integer> columns, Session session) {
-            super(columns, session, SQL_FIXING_COLUMNS_BATCH);
-        }
-
-        @Override
-        protected void fill(SQLQuery query) {
-            query
-                .addScalar("wst_id",        StandardBasicTypes.INTEGER)
-                .addScalar("wst_column_id", StandardBasicTypes.INTEGER)
-                .addScalar("start_time",    StandardBasicTypes.TIMESTAMP)
-                .addScalar("name",          StandardBasicTypes.STRING);
-
-            int lastId = Integer.MIN_VALUE;
-            List<FixColumn> cols = new ArrayList<FixColumn>();
-
-            List<Object []> columns = query.list();
-            for (Object [] c: columns) {
-                int wid = (Integer)c[0];
-
-                if (wid != lastId && !cols.isEmpty()) {
-                    cache(lastId, cols);
-                    cols = new ArrayList<FixColumn>();
-                }
-                cols.add(new FixColumn(
-                    (Integer)c[1],
-                    (Date)   c[2],
-                    (String) c[3]));
-
-                lastId = wid;
-            }
-            if (!cols.isEmpty()) {
-                cache(lastId, cols);
-            }
-        }
-    } // class FixColumnLoader
-
-    public static class QRange extends Range {
-
-        protected double q;
-
-        public QRange() {
-        }
-
-        public QRange(double start, double end, double q) {
-            super(start, end);
-            this.q = q;
-        }
-    } // class QRange
-
-    public static class SectorRange extends Range {
-
-        protected int sector;
-
-        public SectorRange() {
-        }
-
-        public SectorRange(SectorRange other) {
-            start  = other.start;
-            end    = other.end;
-            sector = other.sector;
-        }
-
-        public SectorRange(Range range) {
-            super(range);
-        }
-
-        public SectorRange(double start, double end, int sector) {
-            super(start, end);
-            this.sector = sector;
-        }
-
-        public int getSector() {
-            return sector;
-        }
-
-        public void setSector(int sector) {
-            this.sector = sector;
-        }
-
-        public boolean enlarge(SectorRange other) {
-            if (sector == other.sector
-            && Math.abs(end-other.start) < FixingsOverview.EPSILON) {
-                end = other.end;
-                return true;
-            }
-            return false;
-        }
-    } // class SectorRange
-
-    public static class Fixing implements Serializable {
-
-        public static final Comparator<Column> DATE_CMP =
-            new Comparator<Column>() {
-                @Override
-                public int compare(Column a, Column b) {
-                    return a.startTime.compareTo(b.startTime);
-                }
-            };
-
-        public interface Filter {
-
-            boolean accept(Column column);
-
-        } // interface Filter
-
-        public class Column extends Range {
-
-            protected int    columnId;
-            protected Date   startTime;
-            protected String name;
-
-            protected List<SectorRange> sectors;
-
-            public Column() {
-            }
-
-            public Column(int columnId, Date startTime, String name) {
-                this.columnId  = columnId;
-                this.startTime = startTime;
-                this.name      = name;
-
-                sectors = new ArrayList<SectorRange>();
-            }
-
-            public int getId() {
-                return columnId;
-            }
-
-            public Fixing getFixing() {
-                return Fixing.this;
-            }
-
-            public Date getStartTime() {
-                return startTime;
-            }
-
-            public String getName() {
-                return name;
-            }
-
-            public String getDescription() {
-                return Fixing.this.description + "/" + name;
-            }
-
-            public List<SectorRange> getSectors() {
-                return sectors;
-            }
-
-            public boolean hasSectorsInRange(Range range) {
-                for (SectorRange sector: sectors) {
-                    if (sector.intersects(range)) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-
-            public List<SectorRange> getSectors(Range range) {
-
-                List<SectorRange> result =
-                    new ArrayList<SectorRange>(sectors.size());
-
-                for (SectorRange src: sectors) {
-                    SectorRange dst = new SectorRange(src);
-                    if (range == null || dst.clip(range)) {
-                        result.add(dst);
-                    }
-                }
-
-                return result;
-            }
-
-            public int findQSector(double km) {
-                for (SectorRange sector: sectors) {
-                    if (sector.inside(km)) {
-                        return sector.getSector();
-                    }
-                }
-                return -1;
-            }
-
-            public void buildSectors(
-                GaugeFinder  gaugeFinder,
-                List<QRange> qRanges
-            ) {
-                for (QRange qRange: qRanges) {
-                    for (GaugeRange gRange: gaugeFinder.getGauges()) {
-                        SectorRange sector = new SectorRange(qRange);
-                        if (!sector.clip(gRange)) {
-                            continue;
-                        }
-                        sector.setSector(gRange.classify(qRange.q));
-
-                        if (sectors.isEmpty()
-                        || !sectors.get(sectors.size()-1).enlarge(sector)) {
-                            sectors.add(sector);
-                        }
-                    } // for all gauges
-                } // for all Q ranges
-            }
-
-            public void loadKmRange(KMRangeLoader loader) {
-
-                double [] range = loader.get(columnId);
-
-                if (range == null) {
-                    log.warn("No km range for column " + columnId + ".");
-                    return;
-                }
-                start = range[0];
-                end   = range[1];
-            }
-
-            public void loadQRanges(
-                ColumnQRangeLoader loader,
-                GaugeFinder        gaugeFinder
-            ) {
-                List<double []> qrs = loader.get(columnId);
-                if (qrs == null) {
-                    log.warn("No q ranges found for column " + columnId);
-                    return;
-                }
-
-                List<QRange> qRanges = new ArrayList<QRange>(qrs.size());
-
-                for (double [] qr: qrs) {
-                    double q     = qr[0];
-                    double start = qr[1];
-                    double end   = qr[2];
-
-                    QRange qRange = new QRange(start, end, q);
-                    if (qRange.clip(this)) {
-                        qRanges.add(qRange);
-                    }
-                }
-
-                buildSectors(gaugeFinder, qRanges);
-            }
-        } // class Column
-
-        protected int          wstId;
-        protected String       description;
-        protected List<Column> columns;
-
-        public Fixing() {
-        }
-
-        public int getId() {
-            return wstId;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public Fixing(int wstId, String description) {
-            this.wstId       = wstId;
-            this.description = description;
-            columns = new ArrayList<Column>();
-        }
-
-        public void allColumnIds(List<Integer> cIds) {
-            for (Column column: columns) {
-                cIds.add(column.columnId);
-            }
-        }
-
-        public void loadColumns(FixColumnLoader loader) {
-            List<FixColumn> fcs = loader.get(wstId);
-            if (fcs == null) {
-                log.warn("No columns for wst " + wstId);
-                return;
-            }
-            for (FixColumn fc: fcs) {
-                columns.add(new Column(fc.columnId, fc.startTime, fc.name));
-            }
-        }
-
-        public void loadColumnsKmRange(KMRangeLoader loader) {
-            for (Column column: columns) {
-                column.loadKmRange(loader);
-            }
-        }
-
-        public void adjustExtent(Range extent) {
-            for (Column column: columns) {
-                extent.extend(column);
-            }
-        }
-
-        public void loadColumnsQRanges(
-            ColumnQRangeLoader loader,
-            GaugeFinder        gaugeFinder
-        ) {
-            for (Column column: columns) {
-                column.loadQRanges(loader, gaugeFinder);
-            }
-        }
-
-        /**
-         * @param allColumns[out] Columns will be put here.
-         * @param range can be null.
-         * @param filter filter to apply.
-         */
-        public void addAllColumns(
-            List<Column> allColumns,
-            Range        range,
-            Filter       filter
-        ) {
-            for (Column column: columns) {
-                if ((range == null || column.hasSectorsInRange(range))
-                && (filter == null || filter.accept(column))) {
-                    allColumns.add(column);
-                }
-            }
-        }
-    } // class Fixing
-
-
-    protected String       riverName;
-    protected int          riverId;
-    protected boolean      isKmUp;
-    protected List<Fixing> fixings;
-    protected Range        extent;
-
-    public FixingsOverview() {
-        fixings = new ArrayList<Fixing>();
-        extent  = new Range(Double.MAX_VALUE, -Double.MAX_VALUE);
-    }
-
-    public FixingsOverview(String riverName) {
-        this();
-        this.riverName = riverName;
-    }
-
-    protected boolean loadRiver(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_RIVER_ID)
-            .addScalar("river_id", StandardBasicTypes.INTEGER)
-            .addScalar("km_up",    StandardBasicTypes.BOOLEAN);
-
-        query.setString("name", riverName);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("No river '" + riverName + "' found.");
-            return false;
-        }
-
-        Object [] row = list.get(0);
-
-        riverId = (Integer)row[0];
-        isKmUp  = (Boolean)row[1];
-
-        return true;
-    }
-
-    protected void loadFixings(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_FIXINGS)
-            .addScalar("wst_id",      StandardBasicTypes.INTEGER)
-            .addScalar("description", StandardBasicTypes.STRING);
-
-        query.setInteger("river_id", riverId);
-
-        List<Object []> list = query.list();
-
-        if (list.isEmpty()) {
-            log.warn("River " + riverId + " has no fixings.");
-            // Its pretty fine to have no fixings.
-        }
-
-        for (Object [] row: list) {
-            int    wstId       = (Integer)row[0];
-            String description = (String) row[1];
-            Fixing fixing = new Fixing(wstId, description);
-            fixings.add(fixing);
-        }
-    }
-
-    protected void loadFixingsColumns(Session session) {
-
-        FixColumnLoader loader = new FixColumnLoader(
-            allFixingIds(),
-            session);
-
-        for (Fixing fixing: fixings) {
-            fixing.loadColumns(loader);
-        }
-    }
-
-    protected List<Integer> allFixingIds() {
-        List<Integer> ids = new ArrayList<Integer>(fixings.size());
-        for (Fixing fixing: fixings) {
-            ids.add(fixing.getId());
-        }
-        return ids;
-    }
-
-    protected List<Integer> allColumnIds() {
-        List<Integer> cIds = new ArrayList<Integer>();
-        for (Fixing fixing: fixings) {
-            fixing.allColumnIds(cIds);
-        }
-        return cIds;
-    }
-
-    protected void loadFixingsColumnsKmRange(Session session) {
-
-        KMRangeLoader loader = new KMRangeLoader(
-            allColumnIds(),
-            session);
-
-        for (Fixing fixing: fixings) {
-            fixing.loadColumnsKmRange(loader);
-        }
-    }
-
-    protected void loadFixingsColumnsQRanges(
-        Session     session,
-        GaugeFinder gaugeFinder
-    ) {
-
-        ColumnQRangeLoader loader = new ColumnQRangeLoader(
-            allColumnIds(),
-            session);
-
-        for (Fixing fixing: fixings) {
-            fixing.loadColumnsQRanges(loader, gaugeFinder);
-        }
-    }
-
-    protected void adjustExtent() {
-        for (Fixing fixing: fixings) {
-            fixing.adjustExtent(extent);
-        }
-    }
-
-    public boolean load(Session session) {
-
-        if (!loadRiver(session)) {
-            return false;
-        }
-
-        GaugeFinderFactory gff = GaugeFinderFactory.getInstance();
-
-        GaugeFinder gaugeFinder = gff.getGaugeFinder(riverId, isKmUp);
-
-        if (gaugeFinder == null) {
-            return false;
-        }
-
-        loadFixings(session);
-        loadFixingsColumns(session);
-        loadFixingsColumnsKmRange(session);
-
-        adjustExtent();
-
-        loadFixingsColumnsQRanges(session, gaugeFinder);
-
-        return true;
-    }
-
-    public static final Range FULL_EXTENT =
-        new Range(-Double.MAX_VALUE, Double.MAX_VALUE);
-
-    public static final Fixing.Filter ACCEPT = new Fixing.Filter() {
-        @Override
-        public boolean accept(Fixing.Column column) {
-            return true;
-        }
-    };
-
-    public static class NotFilter implements Fixing.Filter {
-        protected Fixing.Filter child;
-
-        public NotFilter(Fixing.Filter child) {
-            this.child = child;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            return !child.accept(column);
-        }
-    } // class NotFilter
-
-    public static abstract class ComponentFilter implements Fixing.Filter {
-        protected List<Fixing.Filter> children;
-
-        public ComponentFilter() {
-            children = new ArrayList<Fixing.Filter>();
-        }
-
-        public ComponentFilter(List<Fixing.Filter> children) {
-            this.children = children;
-        }
-
-        public ComponentFilter add(Fixing.Filter filter) {
-            children.add(filter);
-            return this;
-        }
-    } // class ComponentFilter
-
-    public static class OrFilter extends ComponentFilter {
-
-        public OrFilter() {
-        }
-
-        public OrFilter(List<Fixing.Filter> children) {
-            super(children);
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (Fixing.Filter child: children) {
-                if (child.accept(column)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    } // class OrFilter
-
-    public static class AndFilter extends ComponentFilter {
-
-        public AndFilter() {
-        }
-
-        public AndFilter(List<Fixing.Filter> children) {
-            super(children);
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (Fixing.Filter child: children) {
-                if (!child.accept(column)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    } // class AndFilter
-
-    public static class IdFilter implements Fixing.Filter {
-
-        protected int columnId;
-
-        public IdFilter(int columnId) {
-            this.columnId = columnId;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            return column.getId() == columnId;
-        }
-    } // class IdFilter
-
-    /** Accept Fixing columns whose id is in id list. */
-    public static class IdsFilter implements Fixing.Filter {
-
-        protected int [] columnIds;
-
-        public IdsFilter(int [] columnIds) {
-            this.columnIds = columnIds;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            int cid = column.getId();
-            for (int i = columnIds.length-1; i >= 0; --i) {
-                if (columnIds[i] == cid) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    } // class IdFilter
-
-    public static class DateFilter implements Fixing.Filter {
-
-        protected Date date;
-
-        public DateFilter(Date date) {
-            this.date = date;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            return date.equals(column.getStartTime());
-        }
-    } // class DateFilter
-
-    public static class DateRangeFilter implements Fixing.Filter {
-
-        protected Date start;
-        protected Date end;
-
-        public DateRangeFilter(Date start, Date end) {
-            if (start.before(end)) {
-                this.start = start;
-                this.end   = end;
-            }
-            else {
-                this.start = end;
-                this.end   = start;
-            }
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            Date date = column.getStartTime();
-            // start <= date <= end
-            return !(date.before(start) || date.after(end));
-        }
-    } // class DateRangeFilter
-
-    public static class SectorFilter implements Fixing.Filter {
-
-        protected int sector;
-
-        public SectorFilter(int sector) {
-            this.sector = sector;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (SectorRange s: column.getSectors()) {
-                if (s.getSector() == sector) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    } // class SectorFilter
-
-    public static class SectorRangeFilter implements Fixing.Filter {
-
-        protected int min;
-        protected int max;
-
-        public SectorRangeFilter(int min, int max) {
-            this.min = Math.min(min, max);
-            this.max = Math.max(min, max);
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (SectorRange s: column.getSectors()) {
-                int v = s.getSector();
-                if (v < min || v > max) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    } // class SectorRangeFilter
-
-    public static class KmFilter implements Fixing.Filter {
-
-        protected double km;
-
-        public KmFilter(double km) {
-            this.km = km;
-        }
-
-        @Override
-        public boolean accept(Fixing.Column column) {
-            for (SectorRange s: column.getSectors()) {
-                if (s.inside(km)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    } // class KmFilter
-
-    public void generateOverview(Document document) {
-        generateOverview(document, FULL_EXTENT, ACCEPT);
-    }
-
-    /**
-     * @param range can be null.
-     */
-    public List<Fixing.Column> filter(Range range, Fixing.Filter filter) {
-        List<Fixing.Column> allColumns = new ArrayList<Fixing.Column>();
-
-        for (Fixing fixing: fixings) {
-            fixing.addAllColumns(allColumns, range, filter);
-        }
-
-        Collections.sort(allColumns, Fixing.DATE_CMP);
-
-        return allColumns;
-    }
-
-    protected static Range realRange(List<Fixing.Column> columns) {
-        Range range = null;
-        for (Fixing.Column column: columns) {
-            if (range == null) {
-                range = new Range(column);
-            }
-            else {
-                range.extend(column);
-            }
-        }
-        return range;
-    }
-
-    protected Element intersectingGauges(Document document, Range range) {
-        Element gauges = document.createElement("gauges");
-
-        if (range == null) {
-            return gauges;
-        }
-
-        GaugeFinderFactory gff = GaugeFinderFactory.getInstance();
-
-        GaugeFinder gf = gff.getGaugeFinder(riverId, isKmUp);
-
-        if (gf == null) {
-            return gauges;
-        }
-
-        for (GaugeRange gr: gf.getGauges()) {
-            if (gr.intersects(range)) {
-                Element gauge = document.createElement("gauge");
-                gauge.setAttribute("from", String.valueOf(gr.getStart()));
-                gauge.setAttribute("to",   String.valueOf(gr.getEnd()));
-                gauge.setAttribute("name", gr.getName());
-                gauges.appendChild(gauge);
-            }
-        }
-
-        return gauges;
-    }
-
-    /** Populate document with fixings, filtered by range and filter. */
-    public void generateOverview(
-        Document      document,
-        Range         range,
-        Fixing.Filter filter
-    ) {
-        List<Fixing.Column> allColumns = filter(range, filter);
-
-        Element fixingsElement = document.createElement("fixings");
-
-        Element riverElement = document.createElement("river");
-
-        riverElement.setAttribute("from", String.valueOf(extent.start));
-        riverElement.setAttribute("to",   String.valueOf(extent.end));
-        riverElement.setAttribute("rid",  String.valueOf(riverId));
-        riverElement.setAttribute("name", riverName);
-
-        fixingsElement.appendChild(riverElement);
-
-        fixingsElement.appendChild(
-            intersectingGauges(
-                document,
-                realRange(allColumns)));
-
-        SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
-
-        Element esE = document.createElement("events");
-
-        for (Fixing.Column column: allColumns) {
-
-            List<SectorRange> sectors = column.getSectors(range);
-
-            if (!sectors.isEmpty()) {
-                Element eE = document.createElement("event");
-                eE.setAttribute("description",
-                    String.valueOf(column.getDescription()));
-                eE.setAttribute("cid", String.valueOf(column.columnId));
-                eE.setAttribute("date", df.format(column.startTime));
-
-                for (SectorRange sector: sectors) {
-                    Element sE = document.createElement("sector");
-
-                    sE.setAttribute("from",  String.valueOf(sector.start));
-                    sE.setAttribute("to",    String.valueOf(sector.end));
-                    sE.setAttribute("class", String.valueOf(sector.sector));
-
-                    eE.appendChild(sE);
-                }
-
-                esE.appendChild(eE);
-            }
-        }
-
-        fixingsElement.appendChild(esE);
-
-        document.appendChild(fixingsElement);
-    }
-
-    public Range getExtent() {
-        return extent;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverviewFactory.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.artifacts.model;
-
-import org.dive4elements.river.artifacts.cache.CacheFactory;
-
-import org.dive4elements.river.backend.SessionHolder;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-
-import org.hibernate.Session;
-
-public class FixingsOverviewFactory
-{
-    private static Logger log = Logger.getLogger(FixingsOverviewFactory.class);
-
-    public static final String CACHE_NAME = "fixings-overviews";
-
-    private FixingsOverviewFactory() {
-    }
-
-
-    public static FixingsOverview getOverview(String river) {
-
-        boolean debug = log.isDebugEnabled();
-
-        if (debug) {
-            log.debug(
-                "Looking for fixings overview for river '" + river + "'");
-        }
-
-        Cache cache = CacheFactory.getCache(CACHE_NAME);
-
-        if (cache == null) {
-            if (debug) {
-                log.debug("Cache not configured.");
-            }
-            return getUncached(river);
-        }
-
-        String key = "fix-over-" + river;
-
-        Element element = cache.get(key);
-
-        if (element != null) {
-            if (debug) {
-                log.debug("Overview found in cache");
-            }
-            return (FixingsOverview)element.getValue();
-        }
-
-        FixingsOverview overview = getUncached(river);
-
-        if (overview != null) {
-            if (debug) {
-                log.debug("Store overview in cache.");
-            }
-            cache.put(new Element(key, overview));
-        }
-
-        return overview;
-    }
-
-    public static FixingsOverview getUncached(String river) {
-        FixingsOverview overview = new FixingsOverview(river);
-
-        Session session = SessionHolder.HOLDER.get();
-
-        return overview.load(session) ? overview : null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeFinder.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/GaugeFinder.java	Thu Aug 16 16:27:53 2018 +0200
@@ -83,7 +83,7 @@
     }
 
     public GaugeRange find(Range range) {
-        return find(isKmUp ? range.start : range.end);
+        return find(isKmUp ? range.getStart() : range.getEnd());
     }
 
     public GaugeRange find(int gaugeId) {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/OfficialLineFinder.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/OfficialLineFinder.java	Thu Aug 16 16:27:53 2018 +0200
@@ -115,8 +115,8 @@
                 " pos: " + columnPos +
                 " source: " + source +
                 " date: " + date +
-                " from: " + start +
-                " to: " + end + "]";
+                " from: " + getStart() +
+                " to: " + getEnd() + "]";
         }
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/QW.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.artifacts.model;
-
-import java.io.Serializable;
-
-public class QW
-implements   Serializable
-{
-    protected double q;
-    protected double w;
-
-    public QW() {
-    }
-
-    public QW(double q, double w) {
-        this.q = q;
-        this.w = w;
-    }
-
-    public double getQ() {
-        return q;
-    }
-
-    public void setQ(double q) {
-        this.q = q;
-    }
-
-    public double getW() {
-        return w;
-    }
-
-    public void setW(double w) {
-        this.w = w;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/Range.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/Range.java	Thu Aug 16 16:27:53 2018 +0200
@@ -15,8 +15,8 @@
 
     public static final double EPSILON = 1e-5;
 
-    protected double start;
-    protected double end;
+    private double start;
+    private double end;
 
     public Range() {
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/RangeWithValues.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/RangeWithValues.java	Thu Aug 16 16:27:53 2018 +0200
@@ -34,8 +34,8 @@
 
     @Override
     public String toString() {
-        return new StringBuilder("start=").append(start)
-            .append(" end=" ).append(end)
+        return new StringBuilder("start=").append(getStart())
+            .append(" end=" ).append(getEnd())
             .append(" values=[").append(Arrays.toString(values)).append(']')
             .toString();
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/AnalysisPeriod.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/AnalysisPeriod.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,99 +8,49 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
-import org.dive4elements.river.artifacts.model.DateRange;
-
 import java.io.Serializable;
 
-public class AnalysisPeriod
-implements   Serializable
-{
-    protected DateRange dateRange;
-    protected QWD []    qwds;
-    protected QWD []    qSectorAverages;
-    protected double [] qSectorStdDevs;
+import org.dive4elements.river.artifacts.model.DateRange;
+
+public class AnalysisPeriod implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private DateRange dateRange;
+
+    private QWD[] qSectorAverages;
+
+    private double[] qSectorStdDevs;
+
+    private double maxQ;
 
     public AnalysisPeriod() {
     }
 
-    public AnalysisPeriod(DateRange dateRange) {
-        this.dateRange = dateRange;
-    }
-
-    public AnalysisPeriod(DateRange dateRange, QWD [] qwds) {
-        this(dateRange);
+    public AnalysisPeriod(final DateRange dateRange, final QWD[] qSectorAverages, final double[] qSectorStdDevs, final double maxQ) {
         this.dateRange = dateRange;
-        this.qwds      = qwds;
-    }
-
-    public AnalysisPeriod(
-        DateRange dateRange,
-        QWD []    qwds,
-        QWD []    qSectorAverages,
-        double [] qSectorStdDevs
-    ) {
-        this(dateRange, qwds);
         this.qSectorAverages = qSectorAverages;
-        this.qSectorStdDevs  = qSectorStdDevs;
+        this.qSectorStdDevs = qSectorStdDevs;
+        this.maxQ = maxQ;
     }
 
     public DateRange getDateRange() {
-        return dateRange;
-    }
-
-    public void setDateRange(DateRange dateRange) {
-        this.dateRange = dateRange;
-    }
-
-    public QWD [] getQWDs() {
-        return qwds;
-    }
-
-    public void setQWDs(QWD [] qwds) {
-        this.qwds = qwds;
+        return this.dateRange;
     }
 
-    public QWD [] getQSectorAverages() {
-        return qSectorAverages;
-    }
-
-    public void setQSectorAverages(QWD [] qSectorAverages) {
-        this.qSectorAverages = qSectorAverages;
+    public QWD[] getQSectorAverages() {
+        return this.qSectorAverages;
     }
 
-    public QWD getQSectorAverage(int i) {
-        return qSectorAverages[i];
+    public QWD getQSectorAverage(final int i) {
+        return this.qSectorAverages[i];
     }
 
-    public double [] getQSectorStdDevs() {
-        return qSectorStdDevs;
-    }
-
-    public void setQSectorStdDevs(double [] qSectorStdDevs) {
-        this.qSectorStdDevs = qSectorStdDevs;
-    }
-
-    public double getQSectorStdDev(int i) {
-        return qSectorStdDevs[i];
+    public double getQSectorStdDev(final int i) {
+        return this.qSectorStdDevs[i];
     }
 
     public double getMaxQ() {
-        double maxQ = -Double.MAX_VALUE;
-        if (qwds != null) {
-            for (QWD qwd: qwds) {
-                if (qwd.getQ() > maxQ) {
-                    maxQ = qwd.getQ();
-                }
-            }
-        }
-        if (qSectorAverages != null) {
-            for (QWD qwd: qSectorAverages) {
-                if (qwd != null && qwd.getQ() > maxQ) {
-                    maxQ = qwd.getQ();
-                }
-            }
-        }
-        return maxQ;
+        return this.maxQ;
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ 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/model/fixings/AnalysisPeriodEventResults.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,62 @@
+/** 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.model.fixings;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Event data of analysis events sorted by period, event, station and qwd
+ *
+ * @author Gernot Belger
+ *
+ */
+public class AnalysisPeriodEventResults implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /* Period-Index -> results */
+    private final Map<Integer, FixResultColumns> resultData = new HashMap<>();
+
+    public AnalysisPeriodEventResults() {
+    }
+
+    public Collection<FixResultColumns> getEventResults() {
+        return this.resultData.values();
+    }
+
+    public FixResultColumns getEventResults(final int periodIndex) {
+        return this.resultData.get(periodIndex);
+    }
+
+    public void addQWD(final int periodIndex, final double km, final FixingColumnWithData event, final QWD qwd) {
+
+        final FixResultColumns columns = getOrCreatePeriodResult(periodIndex);
+        columns.addQWD(event, km, qwd);
+    }
+
+    public void sortAll() {
+        for (final FixResultColumns columns : this.resultData.values())
+            columns.sortAll();
+    }
+
+    private FixResultColumns getOrCreatePeriodResult(final int periodIndex) {
+
+        final FixResultColumns existingColumns = this.resultData.get(periodIndex);
+        if (existingColumns != null)
+            return existingColumns;
+
+        final FixResultColumns newColumns = new FixResultColumns();
+        this.resultData.put(periodIndex, newColumns);
+        return newColumns;
+    }
+}
\ 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/model/fixings/ColumnCache.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,41 @@
+/** 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.model.fixings;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class to find the data belonging to meta info more quickly.
+ */
+class ColumnCache {
+
+    private final Map<Integer, FixingColumnWithData> columns;
+
+    public ColumnCache() {
+        this.columns = new HashMap<>();
+    }
+
+    public FixingColumnWithData getColumn(final FixingColumn meta) {
+
+        final Integer key = meta.getId();
+        final FixingColumnWithData column = this.columns.get(key);
+        if (column != null)
+            return column;
+
+        final FixingColumnData data = FixingsColumnFactory.getInstance().getColumnData(meta);
+        if (data == null)
+            return null;
+
+        final FixingColumnWithData newColumn = new FixingColumnWithData(meta, data, this.columns.size());
+        this.columns.put(key, newColumn);
+        return newColumn;
+    }
+}
\ 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/model/fixings/ColumnQRangeLoader.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,56 @@
+/** 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.model.fixings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dive4elements.river.utils.BatchLoader;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+final class ColumnQRangeLoader extends BatchLoader<List<double[]>> {
+
+    private static final String SQL_FIXING_COLUMN_Q_RANGES_BATCH = "SELECT " + "wcqr.wst_column_id AS wst_column_id," + "wqr.q              AS q,"
+            + "r.a                AS start_km," + "r.b                AS stop_km " + "FROM wst_column_q_ranges wcqr "
+            + "JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id = wqr.id " + "JOIN ranges       r   ON wqr.range_id        = r.id " + "WHERE "
+            + "wcqr.wst_column_id IN ($IDS) " + "ORDER BY wcqr.wst_column_id, r.a";
+
+    public ColumnQRangeLoader(final List<Integer> columns, final Session session) {
+        super(columns, session, SQL_FIXING_COLUMN_Q_RANGES_BATCH);
+    }
+
+    @Override
+    protected void fill(final SQLQuery query) {
+        query.addScalar("wst_column_id", StandardBasicTypes.INTEGER).addScalar("q", StandardBasicTypes.DOUBLE).addScalar("start_km", StandardBasicTypes.DOUBLE)
+        .addScalar("stop_km", StandardBasicTypes.DOUBLE);
+
+        int lastId = Integer.MIN_VALUE;
+        List<double[]> column = new ArrayList<>();
+
+        final List<Object[]> ranges = query.list();
+        for (final Object[] r : ranges) {
+            final int cid = (Integer) r[0];
+
+            if (cid != lastId && !column.isEmpty()) {
+                cache(lastId, column);
+                column = new ArrayList<>();
+            }
+            column.add(new double[] { (Double) r[1], (Double) r[2], (Double) r[3] });
+
+            lastId = cid;
+        }
+
+        if (!column.isEmpty()) {
+            cache(lastId, column);
+        }
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/DateUniqueMaker.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.artifacts.model.fixings;
-
-import java.util.Date;
-
-import gnu.trove.TIntObjectHashMap;
-import gnu.trove.TLongHashSet;
-
-public class DateUniqueMaker {
-
-    private TLongHashSet      times;
-    private TIntObjectHashMap already;
-
-    public DateUniqueMaker() {
-        times   = new TLongHashSet();
-        already = new TIntObjectHashMap();
-    }
-
-    public <T extends QWI> void makeUnique(T t) {
-
-        // Map same index to same new value
-        if (already.containsKey(t.index)) {
-            t.date = (Date)already.get(t.index);
-            return;
-        }
-        long time = t.date.getTime();
-        if (!times.add(time)) { // same found before
-            do {
-                time += 30L*1000L; // Add 30secs
-            }
-            while (!times.add(time));
-            Date newDate = new Date(time);
-            already.put(t.index, newDate);
-            // Write back modified time.
-            t.date = newDate;
-        }
-        else {
-            // register as seen.
-            already.put(t.index, t.date);
-        }
-    }
-}
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/Fitting.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/Fitting.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,9 +8,8 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
-import gnu.trove.TDoubleArrayList;
-
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 import org.apache.commons.math.MathException;
@@ -18,7 +17,6 @@
 import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
 import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
 import org.apache.log4j.Logger;
-
 import org.dive4elements.river.artifacts.math.GrubbsOutlier;
 import org.dive4elements.river.artifacts.math.fitting.Function;
 
@@ -30,79 +28,86 @@
         QWD create(double q, double w, double deltaW, boolean isOutlier);
     }
 
+    private static final class FittingData {
+        public final FixingColumnWithData event;
+        public final double w;
+        public final double q;
+        public final boolean isInterpolated;
+
+        public FittingData(final FixingColumnWithData event, final double w, final double q, final boolean isInterpolated) {
+            this.event = event;
+            this.w = w;
+            this.q = q;
+            this.isInterpolated = isInterpolated;
+        }
+    }
+
     private final double chiSqr;
 
     private final double[] parameters;
 
     private final double standardDeviation;
 
-    private final List<QWD> qwds;
+    private final double maxQ;
 
-    public Fitting(final double[] parameters, final double standardDeviation, final double chiSqr, final List<QWD> qwds) {
+    public Fitting(final double[] parameters, final double standardDeviation, final double chiSqr, final double maxQ) {
         this.parameters = parameters;
         this.standardDeviation = standardDeviation;
         this.chiSqr = chiSqr;
-        this.qwds = qwds;
+        this.maxQ = maxQ;
     }
 
     public double getChiSquare() {
-        return chiSqr;
-    }
-
-    /**
-     * Returns all referenced and outliers as one array.
-     */
-    public QWD[] getFixingsArray() {
-        return qwds.toArray(new QWD[qwds.size()]);
+        return this.chiSqr;
     }
 
     public double getMaxQ() {
-        double maxQ = -Double.MAX_VALUE;
-
-        for (QWD qw : qwds) {
-            final double q = qw.getQ();
-            if (!qw.isOutlier() && q > maxQ)
-                maxQ = q;
-        }
-
-        return maxQ;
+        return this.maxQ;
     }
 
     public double[] getParameters() {
-        return parameters;
+        return this.parameters;
     }
 
     public double getStandardDeviation() {
-        return standardDeviation;
+        return this.standardDeviation;
     }
 
-    public static Fitting fit(final Function function, final QWDFactory qwdFactory, final boolean checkOutliers, final double[] qs, final double[] ws) {
+    public static Fitting fit(final FixResultColumns resultColumns, final double km, final Function function, final boolean checkOutliers,
+            final List<FixingColumnWithData> eventColumns) {
 
-        final TDoubleArrayList xs = new TDoubleArrayList(qs.length);
-        final TDoubleArrayList ys = new TDoubleArrayList(ws.length);
+        final int numEvents = eventColumns.size();
+        final List<FittingData> data = new ArrayList<>(numEvents);
 
-        for (int i = 0; i < qs.length; ++i) {
-            if (!Double.isNaN(qs[i]) && !Double.isNaN(ws[i])) {
-                xs.add(qs[i]);
-                ys.add(ws[i]);
+        final double[] wTemp = new double[1];
+
+        for (final FixingColumnWithData event : eventColumns) {
+
+            final double q = event.getQ(km);
+            if (!Double.isNaN(q)) {
+                final boolean isInterpolated = !event.getW(km, wTemp, 0);
+                final double w = wTemp[0];
+
+                if (!Double.isNaN(w))
+                    data.add(new FittingData(event, w, q, isInterpolated));
             }
         }
 
-        if (xs.size() < 2) {
-            log.warn("Too less points.");
+        if (data.size() < 2) {
+            log.warn("Not enough data for fitting.");
             return null;
         }
 
-        final List<Double> inputs = new ArrayList<>(xs.size());
-        final List<QWD> qwds = new ArrayList<>(xs.size());
-        final List<QWD> outliers = new ArrayList<>(xs.size());
+        final List<FittingData> outliers = new ArrayList<>(data.size());
 
         org.dive4elements.river.artifacts.math.Function instance = null;
         LevenbergMarquardtOptimizer lmo = null;
         double[] parameters = null;
 
         for (;;) {
+
             parameters = null;
+
             for (double tolerance = 1e-10; tolerance < 1e-1; tolerance *= 10d) {
 
                 lmo = new LevenbergMarquardtOptimizer();
@@ -110,17 +115,16 @@
                 lmo.setOrthoTolerance(tolerance);
                 lmo.setParRelativeTolerance(tolerance);
 
-                CurveFitter cf = new CurveFitter(lmo);
+                try {
+                    final CurveFitter cf = new CurveFitter(lmo);
 
-                for (int i = 0, N = xs.size(); i < N; ++i) {
-                    cf.addObservedPoint(xs.getQuick(i), ys.getQuick(i));
-                }
+                    for (final FittingData fittingData : data)
+                        cf.addObservedPoint(fittingData.q, fittingData.w);
 
-                try {
                     parameters = cf.fit(function, function.getInitialGuess());
                     break;
                 }
-                catch (MathException me) {
+                catch (final MathException me) {
                     if (log.isDebugEnabled()) {
                         log.debug("tolerance " + tolerance + " + failed.", me);
                     }
@@ -137,20 +141,21 @@
                 return null;
             }
 
-            // This is the paraterized function for a given km.
+            // This is the parameterized function for a given km.
             instance = function.instantiate(parameters);
 
             if (!checkOutliers)
                 break;
 
-            inputs.clear();
+            /* find the outlier */
+            final List<Double> inputs = new ArrayList<>(data.size());
+            for (final FittingData fittingData : data) {
 
-            for (int i = 0, N = xs.size(); i < N; ++i) {
-                double y = instance.value(xs.getQuick(i));
-                if (Double.isNaN(y)) {
+                double y = instance.value(fittingData.q);
+                if (Double.isNaN(y))
                     y = Double.MAX_VALUE;
-                }
-                inputs.add(Double.valueOf(ys.getQuick(i) - y));
+
+                inputs.add(Double.valueOf(fittingData.w - y));
             }
 
             final Integer outlier = GrubbsOutlier.findOutlier(inputs);
@@ -158,41 +163,59 @@
                 break;
 
             final int idx = outlier.intValue();
-            outliers.add(qwdFactory.create(xs.getQuick(idx), ys.getQuick(idx), Double.NaN, true));
-            xs.remove(idx);
-            ys.remove(idx);
-        }
-        
-        for (QWD outlier : outliers) {
-            
-            final double w = outlier.getW();
-            final double q = outlier.getQ();
-            
-            final double dw = (w - instance.value(q)) * 100.0;
 
-            outlier.setDeltaW(dw);
-            
-            qwds.add(outlier);
+            // outliers.add(qwdFactory.create(xs.getQuick(idx), ys.getQuick(idx), Double.NaN, true));
+            final FittingData removed = data.remove(idx);
+            outliers.add(removed);
         }
 
-        final StandardDeviation stdDev = new StandardDeviation();
-
-        for (int i = 0; i < xs.size(); ++i) {
-
-            final QWD qwd = qwdFactory.create(xs.getQuick(i), ys.getQuick(i), Double.NaN, false);
+        /* now build result data */
+        final List<QWD> qwds = new ArrayList<>(data.size());
 
-            double dw = (qwd.getW() - instance.value(qwd.getQ())) * 100.0;
-            qwd.setDeltaW(dw);
-            
+        /* calculate dW of outliers against the resulting function and add them to results */
+        for (final FittingData outlier : outliers) {
+            final QWD qwd = createQWD(outlier, instance, true);
             qwds.add(qwd);
-            
-            stdDev.increment(dw);
+            resultColumns.addQWD(outlier.event, km, qwd);
+        }
+
+        /*
+         * calculate dW of used values against the resulting function and add them to results , also calculate standard *
+         * deviation
+         */
+        final StandardDeviation stdDev = new StandardDeviation();
+        double maxQ = -Double.MAX_VALUE;
+
+        for (final FittingData fittingData : data) {
+
+            final QWD qwd = createQWD(fittingData, instance, false);
+            qwds.add(qwd);
+            resultColumns.addQWD(fittingData.event, km, qwd);
+
+            stdDev.increment(qwd.getDeltaW());
+
+            final double q = qwd.getQ();
+            if (q > maxQ)
+                maxQ = q;
         }
 
         final double standardDeviation = stdDev.getResult();
 
         final double chiSqr = lmo.getChiSquare();
 
-        return new Fitting(parameters, standardDeviation, chiSqr, qwds);
+        return new Fitting(parameters, standardDeviation, chiSqr, maxQ);
+    }
+
+    private static QWD createQWD(final FittingData data, final org.dive4elements.river.artifacts.math.Function function, final boolean isOutlier) {
+
+        final FixingColumnWithData event = data.event;
+        final Date date = event.getDate();
+        final boolean isInterpolated = data.isInterpolated;
+
+        final double w = data.w;
+        final double q = data.q;
+        final double dw = (w - function.value(q)) * 100.0;
+
+        return new QWD(q, w, date, isInterpolated, dw, isOutlier);
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,56 +8,47 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
-import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
 
+import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
+import org.apache.log4j.Logger;
+import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
 import org.dive4elements.river.artifacts.math.fitting.Function;
-
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.DateRange;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview.AndFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.DateRangeFilter;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
-import org.dive4elements.river.artifacts.model.FixingsOverview.IdsFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.KmFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.SectorFilter;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview;
 import org.dive4elements.river.artifacts.model.Parameters;
 import org.dive4elements.river.artifacts.model.Range;
-
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.AndFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.DateRangeFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.IdsFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.KmFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.SectorFilter;
 import org.dive4elements.river.utils.DateAverager;
 import org.dive4elements.river.utils.KMIndex;
 
 import gnu.trove.TIntIntHashMap;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+public class FixAnalysisCalculation extends FixCalculation {
 
-import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
+    private static final long serialVersionUID = 1L;
 
-import org.apache.log4j.Logger;
-
-public class FixAnalysisCalculation
-extends      FixCalculation
-{
     private static Logger log = Logger.getLogger(FixAnalysisCalculation.class);
 
-    protected DateRange    referencePeriod;
-    protected DateRange [] analysisPeriods;
+    private DateRange referencePeriod;
+
+    private DateRange[] analysisPeriods;
 
     public FixAnalysisCalculation() {
     }
 
-    public FixAnalysisCalculation(FixAnalysisAccess access) {
+    public FixAnalysisCalculation(final FixAnalysisAccess access) {
+
         super(access);
 
-        DateRange    referencePeriod = access.getReferencePeriod();
-        DateRange [] analysisPeriods = access.getAnalysisPeriods();
+        final DateRange referencePeriod = access.getReferencePeriod();
+        final DateRange[] analysisPeriods = access.getAnalysisPeriods();
 
         if (referencePeriod == null) {
             addProblem("fix.missing.reference.period");
@@ -74,132 +65,93 @@
     }
 
     @Override
-    public CalculationResult innerCalculate(
-        FixingsOverview overview,
-        Function        func
-    ) {
-        ColumnCache cc = new ColumnCache();
-
-        FitResult fitResult = doFitting(overview, cc, func);
-
-        if (fitResult == null) {
-            return new CalculationResult(this);
-        }
+    public CalculationResult innerCalculate(final FixingsOverview overview, final Function func) {
+        final ColumnCache cc = new ColumnCache();
 
-        KMIndex<AnalysisPeriod []> analysisPeriods =
-            calculateAnalysisPeriods(
-                func,
-                fitResult.getParameters(),
-                overview,
-                cc);
-
-        analysisPeriods.sort();
+        final FitResult fitResult = doFitting(overview, cc, func);
 
-        FixAnalysisResult far = new FixAnalysisResult(
-            fitResult.getParameters(),
-            fitResult.getFixings(),
-            analysisPeriods);
+        if (fitResult == null)
+            return new CalculationResult(this);
 
-        // Workaraound to deal with same dates in data set
-        far.makeAnalysisEventsUnique();
-        for (int i = 0; i < this.analysisPeriods.length; ++i) {
-            far.remapAnalysisEventsIndicesToRank(i);
-        }
+        final Parameters parameters = fitResult.getParameters();
 
+        final AnalysisPeriodEventResults eventResults = new AnalysisPeriodEventResults();
+        final KMIndex<AnalysisPeriod[]> kmResults = new KMIndex<>(parameters.size());
+
+        calculateAnalysisPeriods(func, parameters, overview, cc, eventResults, kmResults);
+        eventResults.sortAll();
+        kmResults.sort();
+
+        final FixAnalysisResult far = new FixAnalysisResult(parameters, fitResult.getResultColumns(), kmResults, eventResults);
         return new CalculationResult(far, this);
     }
 
     @Override
-    protected Filter createFilter() {
-        Filter ids = super.createFilter();
-        DateRangeFilter rdf = new DateRangeFilter(
-            referencePeriod.getFrom(),
-            referencePeriod.getTo());
+    protected FixingColumnFilter createFilter() {
+        final FixingColumnFilter ids = super.createFilter();
+        final DateRangeFilter rdf = new DateRangeFilter(this.referencePeriod.getFrom(), this.referencePeriod.getTo());
         return new AndFilter().add(rdf).add(ids);
     }
 
-    protected KMIndex<AnalysisPeriod []> calculateAnalysisPeriods(
-        Function        function,
-        Parameters      parameters,
-        FixingsOverview overview,
-        ColumnCache     cc
-    ) {
-        Range range = new Range(from, to);
-
-        int kmIndex   = parameters.columnIndex("km");
-        int maxQIndex = parameters.columnIndex("max_q");
-
-        final double [] ws = new double[1];
-        final double [] qs = new double[1];
-
-        int [] parameterIndices =
-            parameters.columnIndices(function.getParameterNames());
-
-        double [] parameterValues = new double[parameterIndices.length];
+    private void calculateAnalysisPeriods(final Function function, final Parameters parameters, final FixingsOverview overview, final ColumnCache cc,
+            final AnalysisPeriodEventResults eventResults, final KMIndex<AnalysisPeriod[]> kmResults) {
 
-        DateAverager dateAverager = new DateAverager();
-
-        KMIndex<AnalysisPeriod []> results =
-            new KMIndex<AnalysisPeriod []>(parameters.size());
-
-        IdsFilter idsFilter = new IdsFilter(events);
+        final Range range = new Range(this.from, this.to);
 
-        TIntIntHashMap [] col2indices =
-            new TIntIntHashMap[analysisPeriods.length];
-
-        DateRangeFilter [] drfs = new DateRangeFilter[analysisPeriods.length];
+        final int kmIndex = parameters.columnIndex("km");
+        final int maxQIndex = parameters.columnIndex("max_q");
 
-        boolean debug = log.isDebugEnabled();
+        final double[] ws = new double[1];
 
-        for (int i = 0; i < analysisPeriods.length; ++i) {
+        final int[] parameterIndices = parameters.columnIndices(function.getParameterNames());
+
+        final double[] parameterValues = new double[parameterIndices.length];
+
+        final DateAverager dateAverager = new DateAverager();
+
+        final IdsFilter idsFilter = new IdsFilter(this.events);
+
+        final TIntIntHashMap[] col2indices = new TIntIntHashMap[this.analysisPeriods.length];
+
+        final DateRangeFilter[] drfs = new DateRangeFilter[this.analysisPeriods.length];
+
+        final boolean debug = log.isDebugEnabled();
+
+        for (int i = 0; i < this.analysisPeriods.length; ++i) {
             col2indices[i] = new TIntIntHashMap();
-            drfs[i] = new DateRangeFilter(
-                analysisPeriods[i].getFrom(),
-                analysisPeriods[i].getTo());
+            drfs[i] = new DateRangeFilter(this.analysisPeriods[i].getFrom(), this.analysisPeriods[i].getTo());
 
             if (debug) {
-                log.debug("Analysis period " + (i+1) + " date range: " +
-                    analysisPeriods[i].getFrom() + " - " +
-                    analysisPeriods[i].getTo());
+                log.debug("Analysis period " + (i + 1) + " date range: " + this.analysisPeriods[i].getFrom() + " - " + this.analysisPeriods[i].getTo());
             }
         }
 
         for (int row = 0, R = parameters.size(); row < R; ++row) {
-            double km = parameters.get(row, kmIndex);
+            final double km = parameters.get(row, kmIndex);
             parameters.get(row, parameterIndices, parameterValues);
 
             // This is the parameterized function for a given km.
-            org.dive4elements.river.artifacts.math.Function instance =
-                function.instantiate(parameterValues);
-
-            KmFilter kmFilter = new KmFilter(km);
-
-            ArrayList<AnalysisPeriod> periodResults =
-                new ArrayList<AnalysisPeriod>(analysisPeriods.length);
+            final org.dive4elements.river.artifacts.math.Function instance = function.instantiate(parameterValues);
 
-            for (int ap = 0; ap < analysisPeriods.length; ++ap) {
-                DateRange analysisPeriod = analysisPeriods[ap];
-                TIntIntHashMap col2index = col2indices[ap];
+            final KmFilter kmFilter = new KmFilter(km);
 
-                DateRangeFilter drf = drfs[ap];
+            final List<AnalysisPeriod> periodResults = new ArrayList<>(this.analysisPeriods.length);
 
-                QWD []    qSectorAverages = new QWD[4];
-                double [] qSectorStdDevs  = new double[4];
+            for (int ap = 0; ap < this.analysisPeriods.length; ++ap) {
+                final DateRange analysisPeriod = this.analysisPeriods[ap];
 
-                ArrayList<QWD> allQWDs = new ArrayList<QWD>();
+                final DateRangeFilter drf = drfs[ap];
+
+                final QWD[] qSectorAverages = new QWD[4];
+                final double[] qSectorStdDevs = new double[4];
+
+                final ArrayList<QWD> allQWDs = new ArrayList<>();
 
                 // for all Q sectors.
-                for (int qSector = qSectorStart;
-                     qSector <= qSectorEnd;
-                     ++qSector
-                ) {
-                    Filter filter = new AndFilter()
-                        .add(kmFilter)
-                        .add(new SectorFilter(qSector))
-                        .add(drf)
-                        .add(idsFilter);
+                for (int qSector = this.qSectorStart; qSector <= this.qSectorEnd; ++qSector) {
+                    final FixingColumnFilter filter = new AndFilter().add(kmFilter).add(new SectorFilter(qSector)).add(drf).add(idsFilter);
 
-                    List<Fixing.Column> metas = overview.filter(range, filter);
+                    final List<FixingColumn> metas = overview.filter(range, filter);
 
                     if (metas.isEmpty()) {
                         // No fixings for km and analysis period
@@ -209,53 +161,42 @@
                     double sumQ = 0.0;
                     double sumW = 0.0;
 
-                    StandardDeviation stdDev = new StandardDeviation();
+                    final StandardDeviation stdDev = new StandardDeviation();
 
-                    List<QWD> qwds = new ArrayList<QWD>(metas.size());
+                    final List<QWD> qwds = new ArrayList<>(metas.size());
 
                     dateAverager.clear();
 
-                    for (Fixing.Column meta: metas) {
+                    for (final FixingColumn meta : metas) {
                         if (meta.findQSector(km) != qSector) {
                             // Ignore not matching sectors.
                             continue;
                         }
 
-                        final Column column = cc.getColumn(meta);
-                        if (column == null )
-                            continue;
-
-                        boolean interpolated = !column.getQW(km, qs, ws, 0);
-                        // FIXME: it was like this before... check if this has an effekt on the calculation
-                        interpolated = true;
-
-                        final double w = ws[0];
-                        final double q = qs[0];
-                        if( Double.isNaN(w) || Double.isNaN(q))
+                        final FixingColumnWithData column = cc.getColumn(meta);
+                        if (column == null)
                             continue;
 
-                        double fw = instance.value(q);
-                        if (Double.isNaN(fw)) {
+                        final double q = column.getQ(km);
+                        final boolean interpolated = !column.getW(km, ws, 0);
+                        final double w = ws[0];
+
+                        if (Double.isNaN(w) || Double.isNaN(q))
                             continue;
-                        }
 
-                        double dw = (w - fw)*100.0;
+                        final double fw = instance.value(q);
+                        if (Double.isNaN(fw))
+                            continue;
 
-                        
+                        final double dw = (w - fw) * 100.0;
+
                         stdDev.increment(dw);
 
-                        Date date = column.getDate();
-                        String description = column.getDescription();
-                        
-                        
-                        QWD qwd = new QWD(
-                            q, w,
-                            description,
-                            date, interpolated,
-                            dw, getIndex(col2index, column.getIndex()),
-                            false);
+                        final Date date = column.getDate();
 
+                        final QWD qwd = new QWD(q, w, date, interpolated, dw, false);
                         qwds.add(qwd);
+                        eventResults.addQWD(ap, km, column, qwd);
 
                         sumW += w;
                         sumQ += q;
@@ -264,71 +205,58 @@
                     }
 
                     // Calulate average per Q sector.
-                    int N = qwds.size();
+                    final int N = qwds.size();
                     if (N > 0) {
                         allQWDs.addAll(qwds);
-                        double avgW = sumW / N;
-                        double avgQ = sumQ / N;
+                        final double avgW = sumW / N;
+                        final double avgQ = sumQ / N;
 
-                        double avgFw = instance.value(avgQ);
+                        final double avgFw = instance.value(avgQ);
                         if (!Double.isNaN(avgFw)) {
-                            double avgDw = (avgW - avgFw)*100.0;
-                            Date avgDate = dateAverager.getAverage();
+                            final double avgDw = (avgW - avgFw) * 100.0;
+                            final Date avgDate = dateAverager.getAverage();
 
-                            String avgDescription = "avg.deltawt." + qSector;
-
-                            QWD avgQWD = new QWD(
-                                avgQ, avgW, avgDescription,
-                                avgDate, true, avgDw, 0, false);
+                            final QWD avgQWD = new QWD(avgQ, avgW, avgDate, true, avgDw, false);
 
                             qSectorAverages[qSector] = avgQWD;
                         }
                         qSectorStdDevs[qSector] = stdDev.getResult();
-                    }
-                    else {
+                    } else {
                         qSectorStdDevs[qSector] = Double.NaN;
                     }
                 } // for all Q sectors
 
-                QWD [] aqwds = allQWDs.toArray(new QWD[allQWDs.size()]);
+                /* calculate max q for this period */
+                double maxQ = -Double.MAX_VALUE;
+                for (final QWD qwd : allQWDs) {
+                    if (qwd.getQ() > maxQ)
+                        maxQ = qwd.getQ();
+                }
+                for (final QWD qwd : qSectorAverages) {
+                    if (qwd != null && qwd.getQ() > maxQ)
+                        maxQ = qwd.getQ();
+                }
 
-                AnalysisPeriod periodResult = new AnalysisPeriod(
-                    analysisPeriod,
-                    aqwds,
-                    qSectorAverages,
-                    qSectorStdDevs);
+                final AnalysisPeriod periodResult = new AnalysisPeriod(analysisPeriod, qSectorAverages, qSectorStdDevs, maxQ);
                 periodResults.add(periodResult);
             }
 
             double maxQ = -Double.MAX_VALUE;
-            for (AnalysisPeriod ap: periodResults) {
-                double q = ap.getMaxQ();
+            for (final AnalysisPeriod ap : periodResults) {
+                final double q = ap.getMaxQ();
                 if (q > maxQ) {
                     maxQ = q;
                 }
             }
 
-            double oldMaxQ = parameters.get(row, maxQIndex);
+            final double oldMaxQ = parameters.get(row, maxQIndex);
             if (oldMaxQ < maxQ) {
                 parameters.set(row, maxQIndex, maxQ);
             }
 
-            AnalysisPeriod [] rap = new AnalysisPeriod[periodResults.size()];
+            final AnalysisPeriod[] rap = new AnalysisPeriod[periodResults.size()];
             periodResults.toArray(rap);
-            results.add(km, rap);
+            kmResults.add(km, rap);
         }
-
-        return results;
     }
-
-    /** Returns the mapped value of colIdx or the size of the hashmap. */
-    private static final int getIndex(TIntIntHashMap map, int colIdx) {
-        if (map.containsKey(colIdx)) {
-            return map.get(colIdx);
-        }
-        int index = map.size();
-        map.put(colIdx, index);
-        return index;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisEventsFacet.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisEventsFacet.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,115 +8,96 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import org.apache.commons.lang.math.DoubleRange;
+import org.apache.log4j.Logger;
 import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.CallContext;
-
 import org.dive4elements.river.artifacts.D4EArtifact;
-
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.FacetTypes;
-
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 
-import org.dive4elements.river.utils.KMIndex;
-
-import org.apache.log4j.Logger;
-
 /**
  * Facet to show W values for Q values at km for a date.
  *
  * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
  */
-public class FixAnalysisEventsFacet
-extends      FixingsFacet
-implements   FacetTypes {
+public class FixAnalysisEventsFacet extends FixingsEventFacet implements FacetTypes {
+
+    private static final long serialVersionUID = 1L;
 
     /** House log. */
     private static Logger log = Logger.getLogger(FixAnalysisEventsFacet.class);
 
+    private int periodIndex;
+
+    private int columnId;
+
     /** Trivial Constructor. */
     public FixAnalysisEventsFacet() {
     }
 
+    public FixAnalysisEventsFacet(final int facetIndex, final int periodIndex, final int columnId, final String name, final String description,
+            final DoubleRange stationRange) {
+        super(facetIndex, name, description, ComputeType.ADVANCE, null, null, stationRange);
 
-    /**
-     * @param name
-     */
-    public FixAnalysisEventsFacet(int index, String name, String description) {
-        super(index,
-             name,
-             description,
-             ComputeType.ADVANCE,
-             null,
-             null);
+        this.periodIndex = periodIndex;
+        this.columnId = columnId;
     }
 
-
     /**
      * Returns the data this facet requires.
      *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
+     * @param artifact
+     *            the owner artifact.
+     * @param context
+     *            the CallContext (ignored).
      *
      * @return the data.
      */
     @Override
-    public Object getData(Artifact artifact, CallContext context) {
+    public Object getData(final Artifact artifact, final CallContext context) {
         log.debug("FixAnalysisEventsFacet.getData");
 
         if (!(artifact instanceof D4EArtifact)) {
             log.debug("Not an instance of FixationArtifact.");
             return null;
         }
-        D4EArtifact flys = (D4EArtifact)artifact;
-
-        CalculationResult res =
-            (CalculationResult) flys.compute(context,
-                                             ComputeType.ADVANCE,
-                                             false);
 
-        FixAnalysisResult result = (FixAnalysisResult) res.getData();
-        double currentKm = getCurrentKm(context);
+        final D4EArtifact flys = (D4EArtifact) artifact;
 
-        KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
-        KMIndex.Entry<AnalysisPeriod []> kmPeriodsEntry =
-            kmPeriods.binarySearch(currentKm);
+        final CalculationResult res = (CalculationResult) flys.compute(context, ComputeType.ADVANCE, false);
 
-        if (kmPeriodsEntry == null) {
-            log.debug("getData: kmPeriodsEntry == null");
+        final FixAnalysisResult result = (FixAnalysisResult) res.getData();
+        final double currentKm = getCurrentKm(context);
+
+        final AnalysisPeriodEventResults eventResults = result.getAnalysisEventResults();
+        final FixResultColumns eventResult = eventResults.getEventResults(this.periodIndex);
+        if (eventResult == null) {
+            log.error("No event result for period: " + this.periodIndex);
             return null;
         }
 
-        AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
-        if (periods == null) {
-            log.debug("getData: periods == null");
-            return null;
-        }
-        int ndx = index >> 8;
-        QWD[] qwdData = periods[ndx].getQWDs();
-        if (qwdData == null) {
+        final FixResultColumn event = eventResult.getColumn(this.columnId);
+        if (event == null) {
+            log.error("Missing event with columnId: " + this.columnId);
             return null;
         }
-        int ndy = index & 255;
 
-        for (QWD qwd: qwdData) {
-            if (qwd.getIndex() == ndy) {
-                return qwd;
-            }
-        }
-        return null;
+        return event.getQWD(currentKm);
     }
 
-
     /**
      * Create a deep copy of this Facet.
+     *
      * @return a deep copy.
      */
     @Override
     public FixAnalysisEventsFacet deepCopy() {
-        FixAnalysisEventsFacet copy = new FixAnalysisEventsFacet();
+        final FixAnalysisEventsFacet copy = new FixAnalysisEventsFacet();
         copy.set(this);
+        copy.periodIndex = this.periodIndex;
+        copy.columnId = this.columnId;
         return copy;
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisPeriodsFacet.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisPeriodsFacet.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,29 +8,24 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import org.apache.log4j.Logger;
 import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.CallContext;
-
 import org.dive4elements.river.artifacts.D4EArtifact;
-
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.DateRange;
 import org.dive4elements.river.artifacts.model.FacetTypes;
-
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
-
 import org.dive4elements.river.utils.KMIndex;
 
-import org.apache.log4j.Logger;
-
 /**
  * Facet to show W values for Q values at km for a date.
  *
  * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
  */
-public class FixAnalysisPeriodsFacet
-extends      FixingsFacet
-implements   FacetTypes {
+public class FixAnalysisPeriodsFacet extends FixingsFacet implements FacetTypes {
+
+    private static final long serialVersionUID = 1L;
 
     /** House log. */
     private static Logger log = Logger.getLogger(FixAnalysisPeriodsFacet.class);
@@ -39,78 +34,62 @@
     public FixAnalysisPeriodsFacet() {
     }
 
-
-    /**
-     * @param name
-     */
-    public FixAnalysisPeriodsFacet(int index, String name, String description) {
-        super(index,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
+    public FixAnalysisPeriodsFacet(final int index, final String name, final String description) {
+        super(index, name, description, ComputeType.ADVANCE, null, null);
     }
 
-
     /**
      * Returns the data this facet requires.
      *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext (ignored).
+     * @param artifact
+     *            the owner artifact.
+     * @param context
+     *            the CallContext (ignored).
      *
      * @return the data.
      */
     @Override
-    public Object getData(Artifact artifact, CallContext context) {
+    public Object getData(final Artifact artifact, final CallContext context) {
         log.debug("FixAnalysisPeriodsFacet.getData");
 
         if (artifact instanceof D4EArtifact) {
-            D4EArtifact flys = (D4EArtifact)artifact;
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
-            double currentKm = getCurrentKm(context);
+            final D4EArtifact flys = (D4EArtifact) artifact;
 
-            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
-            KMIndex.Entry<AnalysisPeriod []> kmPeriodsEntry =
-                kmPeriods.binarySearch(currentKm);
+            final CalculationResult res = (CalculationResult) flys.compute(context, ComputeType.ADVANCE, false);
 
-            if (kmPeriodsEntry == null) {
+            final FixAnalysisResult result = (FixAnalysisResult) res.getData();
+            final double currentKm = getCurrentKm(context);
+
+            final KMIndex<AnalysisPeriod[]> kmPeriods = result.getAnalysisPeriods();
+            final KMIndex.Entry<AnalysisPeriod[]> kmPeriodsEntry = kmPeriods.binarySearch(currentKm);
+            if (kmPeriodsEntry == null)
                 return null;
-            }
-
-            AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
 
-            if (periods == null) {
+            final AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
+            if (periods == null)
                 return null;
-            }
-            DateRange[] dates = new DateRange[periods.length];
+
+            final DateRange[] dates = new DateRange[periods.length];
             for (int i = 0; i < periods.length; i++) {
                 dates[i] = periods[i].getDateRange();
             }
+
             return dates;
         }
-        else {
-            log.debug("Not an instance of FixationArtifact.");
-            return null;
-        }
+
+        log.debug("Not an instance of FixationArtifact.");
+        return null;
     }
 
-
     /**
      * Create a deep copy of this Facet.
+     *
      * @return a deep copy.
      */
     @Override
     public FixAnalysisPeriodsFacet deepCopy() {
-        FixAnalysisPeriodsFacet copy = new FixAnalysisPeriodsFacet();
+        final FixAnalysisPeriodsFacet copy = new FixAnalysisPeriodsFacet();
         copy.set(this);
         return copy;
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisResult.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisResult.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,46 +8,31 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
-import gnu.trove.TIntObjectHashMap;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import org.apache.log4j.Logger;
 import org.dive4elements.river.artifacts.model.Parameters;
-
 import org.dive4elements.river.utils.KMIndex;
 
-public class FixAnalysisResult
-extends      FixResult
-{
-    private static Logger log =
-        Logger.getLogger(FixAnalysisResult.class);
+public class FixAnalysisResult extends FixResult {
 
-    protected KMIndex<AnalysisPeriod []> analysisPeriods;
+    private static final long serialVersionUID = 1L;
 
-    public FixAnalysisResult() {
+    private final KMIndex<AnalysisPeriod[]> analysisKmPeriods;
+
+    private final AnalysisPeriodEventResults analysisEventResults;
+
+    public FixAnalysisResult(final Parameters parameters, final FixResultColumns fixResultColumns, final KMIndex<AnalysisPeriod[]> analysisKmPeriods,
+            final AnalysisPeriodEventResults analysisEventResults) {
+        super(parameters, fixResultColumns);
+
+        this.analysisKmPeriods = analysisKmPeriods;
+        this.analysisEventResults = analysisEventResults;
     }
 
-    public FixAnalysisResult(
-        Parameters                 parameters,
-        KMIndex<QWD []>            fixings,
-        KMIndex<AnalysisPeriod []> analysisPeriods
-    ) {
-        super(parameters, fixings);
-        this.analysisPeriods = analysisPeriods;
-    }
-
-    public int getUsedSectorsInAnalysisPeriods() {
+    public final int getUsedSectorsInAnalysisPeriods() {
         int result = 0;
-        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
-            for (AnalysisPeriod period: entry.getValue()) {
+        for (final KMIndex.Entry<AnalysisPeriod[]> entry : this.analysisKmPeriods) {
+            for (final AnalysisPeriod period : entry.getValue()) {
                 for (int i = 0; i < 4; ++i) {
-                    result |= period.getQSectorAverage(i) != null
-                        ? (1 << i)
-                        : 0;
+                    result |= period.getQSectorAverage(i) != null ? (1 << i) : 0;
                 }
                 // XXX: Stop early on result == ~(~0 << 4)) ?
             }
@@ -55,76 +40,11 @@
         return result;
     }
 
-
-    public void makeAnalysisEventsUnique() {
-        TIntObjectHashMap dums = new TIntObjectHashMap();
-
-        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
-            AnalysisPeriod [] aps = entry.getValue();
-            for (int i = 0; i < aps.length; ++i) {
-                AnalysisPeriod ap = aps[i];
-                QWD [] qwds = ap.getQWDs();
-                if (qwds == null) {
-                    continue;
-                }
-                DateUniqueMaker dum = (DateUniqueMaker)dums.get(i);
-                if (dum == null) {
-                    dums.put(i, dum = new DateUniqueMaker());
-                }
-                for (QWD qwd: qwds) {
-                    dum.makeUnique(qwd);
-                }
-            }
-        }
-    }
-
-    public Collection<Date> getAnalysisEventsDates(int analysisPeriod) {
-        TreeSet<Date> dates = new TreeSet<Date>();
-        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
-            QWD [] qwds = entry.getValue()[analysisPeriod].getQWDs();
-            if (qwds != null) {
-                for (QWD qwd: qwds) {
-                    dates.add(qwd.date);
-                }
-            }
-        }
-        return dates;
+    public final KMIndex<AnalysisPeriod[]> getAnalysisPeriods() {
+        return this.analysisKmPeriods;
     }
 
-    public Collection<Integer> getAnalysisEventsIndices(int analysisPeriod) {
-        TreeMap<Date, Integer> dates = new TreeMap<Date, Integer>();
-        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
-            QWD [] qwds = entry.getValue()[analysisPeriod].getQWDs();
-            if (qwds != null) {
-                for (QWD qwd: qwds) {
-                    dates.put(qwd.date, qwd.index);
-                }
-            }
-        }
-        return dates.values();
+    public AnalysisPeriodEventResults getAnalysisEventResults() {
+        return this.analysisEventResults;
     }
-
-    public void remapAnalysisEventsIndicesToRank(int analysisPeriod) {
-        RankRemapper remapper = new RankRemapper();
-        for (Integer index: getAnalysisEventsIndices(analysisPeriod)) {
-            remapper.toMap(index);
-        }
-        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
-            QWD [] qwds = entry.getValue()[analysisPeriod].getQWDs();
-            if (qwds != null) {
-                for (QWD qwd: qwds) {
-                    remapper.remap(qwd);
-                }
-            }
-        }
-    }
-
-    public KMIndex<AnalysisPeriod []> getAnalysisPeriods() {
-        return analysisPeriods;
-    }
-
-    public void setAnalysisPeriods(KMIndex<AnalysisPeriod []> analysisPeriods) {
-        this.analysisPeriods = analysisPeriods;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java	Thu Aug 16 16:27:53 2018 +0200
@@ -9,10 +9,7 @@
 package org.dive4elements.river.artifacts.model.fixings;
 
 import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.log4j.Logger;
 import org.dive4elements.artifacts.common.utils.StringUtils;
@@ -21,118 +18,39 @@
 import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
 import org.dive4elements.river.artifacts.model.Calculation;
 import org.dive4elements.river.artifacts.model.CalculationResult;
-import org.dive4elements.river.artifacts.model.FixingsColumn;
-import org.dive4elements.river.artifacts.model.FixingsColumnFactory;
-import org.dive4elements.river.artifacts.model.FixingsOverview;
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
-import org.dive4elements.river.artifacts.model.FixingsOverview.IdsFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
 import org.dive4elements.river.artifacts.model.Parameters;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.IdsFilter;
 import org.dive4elements.river.utils.DoubleUtil;
-import org.dive4elements.river.utils.KMIndex;
 
 /** Calculation base class for fix. */
 public abstract class FixCalculation extends Calculation {
+
+    private static final long serialVersionUID = 1L;
+
     private static Logger log = Logger.getLogger(FixCalculation.class);
 
-    public static final double EPSILON = 1e-4;
-
-    public static final String[] STANDARD_COLUMNS = { "km", "chi_sqr", "max_q", "std-dev" };
+    private static final String[] STANDARD_COLUMNS = { "km", "chi_sqr", "max_q", "std-dev" };
 
     protected static class FitResult {
 
         private final Parameters parameters;
-        private final KMIndex<QWD[]> fixings;
 
-        public FitResult(final Parameters parameters, final KMIndex<QWD[]> fixings) {
+        private final FixResultColumns resultColumns;
+
+        public FitResult(final Parameters parameters, final FixResultColumns resultColumns) {
             this.parameters = parameters;
-            this.fixings = fixings;
+            this.resultColumns = resultColumns;
         }
 
         public Parameters getParameters() {
             return this.parameters;
         }
 
-        public KMIndex<QWD[]> getFixings() {
-            return this.fixings;
+        public FixResultColumns getResultColumns() {
+            return this.resultColumns;
         }
     }
 
-    /**
-     * Helper class to bundle the meta information of a column
-     * and the real data.
-     */
-    protected static class Column {
-
-        protected Fixing.Column meta;
-        protected FixingsColumn data;
-        protected int index;
-
-        public Column() {
-        }
-
-        public Column(final Fixing.Column meta, final FixingsColumn data, final int index) {
-            this.meta = meta;
-            this.data = data;
-            this.index = index;
-        }
-
-        public Date getDate() {
-            return this.meta.getStartTime();
-        }
-
-        public String getDescription() {
-            return this.meta.getDescription();
-        }
-
-        public int getIndex() {
-            return this.index;
-        }
-
-        public int getId() {
-            return this.meta.getId();
-        }
-
-        public boolean getQW(final double km, final double[] qs, final double[] ws, final int index) {
-            qs[index] = this.data.getQ(km);
-            return this.data.getW(km, ws, index);
-        }
-
-        public boolean getQW(final double km, final double[] wq) {
-            this.data.getW(km, wq, 0);
-            if (Double.isNaN(wq[0]))
-                return false;
-            wq[1] = this.data.getQ(km);
-            return !Double.isNaN(wq[1]);
-        }
-    } // class Column
-
-    /**
-     * Helper class to find the data belonging to meta info more quickly.
-     */
-    protected static class ColumnCache {
-
-        protected Map<Integer, Column> columns;
-
-        public ColumnCache() {
-            this.columns = new HashMap<>();
-        }
-
-        public Column getColumn(final Fixing.Column meta) {
-            final Integer key = meta.getId();
-            Column column = this.columns.get(key);
-            if (column == null) {
-                final FixingsColumn data = FixingsColumnFactory.getInstance().getColumnData(meta);
-                if (data != null) {
-                    column = new Column(meta, data, this.columns.size());
-                    this.columns.put(key, column);
-                }
-            }
-            return column;
-        }
-    } // class ColumnCache
-
     protected String river;
     protected double from;
     protected double to;
@@ -220,20 +138,20 @@
      * Create filter to accept only the chosen events.
      * This factored out out to be overwritten.
      */
-    protected Filter createFilter() {
+    protected FixingColumnFilter createFilter() {
         return new IdsFilter(this.events);
     }
 
-    protected List<Column> getEventColumns(final FixingsOverview overview, final ColumnCache cc) {
-        final Filter filter = createFilter();
-
-        final List<Fixing.Column> metas = overview.filter(null, filter);
+    protected List<FixingColumnWithData> getEventColumns(final FixingsOverview overview, final ColumnCache cc) {
+        final FixingColumnFilter filter = createFilter();
 
-        final List<Column> columns = new ArrayList<>(metas.size());
+        final List<FixingColumn> metas = overview.filter(null, filter);
 
-        for (final Fixing.Column meta : metas) {
+        final List<FixingColumnWithData> columns = new ArrayList<>(metas.size());
 
-            final Column data = cc.getColumn(meta);
+        for (final FixingColumn meta : metas) {
+
+            final FixingColumnWithData data = cc.getColumn(meta);
             if (data == null) {
                 addProblem("fix.cannot.load.data");
             } else {
@@ -245,36 +163,18 @@
     }
 
     // Fit a function to the given points from fixation.
-    protected FitResult doFitting(final FixingsOverview overview, final ColumnCache cc, final Function func) {
+    protected final FitResult doFitting(final FixingsOverview overview, final ColumnCache cc, final Function func) {
         final boolean debug = log.isDebugEnabled();
 
-        final List<Column> eventColumns = getEventColumns(overview, cc);
+        final FixResultColumns resultColumns = new FixResultColumns();
+
+        final List<FixingColumnWithData> eventColumns = getEventColumns(overview, cc);
 
         if (eventColumns.size() < 2) {
             addProblem("fix.too.less.data.columns");
             return null;
         }
 
-        final double[] qs = new double[eventColumns.size()];
-        final double[] ws = new double[qs.length];
-        final boolean[] interpolated = new boolean[ws.length];
-
-        final Fitting.QWDFactory qwdFactory = new Fitting.QWDFactory() {
-            @Override
-            public QWD create(final double q, final double w, double deltaW, boolean isOutlier) {
-                // Check all the event columns for close match
-                // and take the description and the date from meta.
-                for (int i = 0; i < qs.length; ++i) {
-                    if (Math.abs(qs[i] - q) < EPSILON && Math.abs(ws[i] - w) < EPSILON) {
-                        final Column column = eventColumns.get(i);
-                        return new QWD(qs[i], ws[i], column.getDescription(), column.getDate(), interpolated[i], deltaW, column.getId(), isOutlier); // Use database id here
-                    }
-                }
-                log.warn("cannot find column for (" + q + ", " + w + ")");
-                return new QWD(q, w, isOutlier);
-            }
-        };
-
         final String[] parameterNames = func.getParameterNames();
 
         final Parameters results = new Parameters(StringUtils.join(STANDARD_COLUMNS, parameterNames));
@@ -287,8 +187,6 @@
             log.debug("number of kms: " + kms.length);
         }
 
-        final KMIndex<QWD[]> fixings = new KMIndex<>();
-
         final int kmIndex = results.columnIndex("km");
         final int chiSqrIndex = results.columnIndex("chi_sqr");
         final int maxQIndex = results.columnIndex("max_q");
@@ -299,20 +197,13 @@
 
         for (final double km : kms) {
 
-            // Fill Qs and Ws from event columns.
-            for (int j = 0; j < ws.length; ++j) 
-                interpolated[j] = !eventColumns.get(j).getQW(km, qs, ws, j);
-
-            final Fitting fitting = Fitting.fit(func, qwdFactory, this.preprocessing, qs, ws);
+            final Fitting fitting = Fitting.fit(resultColumns, km, func, this.preprocessing, eventColumns);
             if (fitting == null) {
                 log.debug("Fitting for km: " + km + " failed");
                 ++numFailed;
                 addProblem(km, "fix.fitting.failed");
                 continue;
             }
-            
-            final QWD[] fixingsArray = fitting.getFixingsArray();
-            fixings.add(km, fixingsArray);
 
             final int row = results.newRow();
             final double[] values = fitting.getParameters();
@@ -338,9 +229,9 @@
             results.removeNaNs();
         }
 
-        fixings.sort();
+        resultColumns.sortAll();
 
-        return new FitResult(results, fixings);
+        return new FitResult(results, resultColumns);
     }
 
     public CalculationResult calculate() {
@@ -356,22 +247,10 @@
             addProblem("fix.invalid.function.name");
         }
 
-        if (hasProblems()) {
+        if (hasProblems())
             return new CalculationResult(this);
-        }
-        final CalculationResult result = innerCalculate(overview, func);
 
-        if (result != null) {
-            // Workaraound to deal with same dates in data set
-            final Object o = result.getData();
-            if (o instanceof FixResult) {
-                final FixResult fr = (FixResult) o;
-                fr.makeEventsDatesUnique();
-                fr.remapEventIndicesToRank();
-            }
-        }
-
-        return result;
+        return innerCalculate(overview, func);
     }
 
     protected abstract CalculationResult innerCalculate(FixingsOverview overview, Function function);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixColumnLoader.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,56 @@
+/** 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.model.fixings;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.FixColumn;
+import org.dive4elements.river.utils.BatchLoader;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+final class FixColumnLoader extends BatchLoader<List<FixColumn>> {
+
+    private static final String SQL_FIXING_COLUMNS_BATCH = "SELECT " + "wc.wst_id     AS wst_id," + "wc.id         AS wst_column_id,"
+            + "ti.start_time AS start_time," + "wc.name       AS name " + "FROM wst_columns wc " + "JOIN time_intervals ti ON wc.time_interval_id = ti.id "
+            + "WHERE " + "wc.wst_id IN ($IDS) " + "ORDER BY wc.wst_id, position";
+
+    public FixColumnLoader(final List<Integer> columns, final Session session) {
+        super(columns, session, SQL_FIXING_COLUMNS_BATCH);
+    }
+
+    @Override
+    protected void fill(final SQLQuery query) {
+        query.addScalar("wst_id", StandardBasicTypes.INTEGER).addScalar("wst_column_id", StandardBasicTypes.INTEGER)
+        .addScalar("start_time", StandardBasicTypes.TIMESTAMP).addScalar("name", StandardBasicTypes.STRING);
+
+        int lastId = Integer.MIN_VALUE;
+        List<FixColumn> cols = new ArrayList<>();
+
+        final List<Object[]> columns = query.list();
+        for (final Object[] c : columns) {
+            final int wid = (Integer) c[0];
+
+            if (wid != lastId && !cols.isEmpty()) {
+                cache(lastId, cols);
+                cols = new ArrayList<>();
+            }
+            cols.add(new FixColumn((Integer) c[1], (Date) c[2], (String) c[3]));
+
+            lastId = wid;
+        }
+        if (!cols.isEmpty()) {
+            cache(lastId, cols);
+        }
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalAnalysisFacet.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalAnalysisFacet.java	Thu Aug 16 16:27:53 2018 +0200
@@ -9,7 +9,6 @@
 package org.dive4elements.river.artifacts.model.fixings;
 
 import org.apache.log4j.Logger;
-
 import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.D4EArtifact;
@@ -17,101 +16,86 @@
 import org.dive4elements.river.artifacts.model.DataFacet;
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
-import org.dive4elements.river.utils.KMIndex;
 
 /**
  * Facet to show average W values for Q sectors.
  *
  * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
  */
-public class FixLongitudinalAnalysisFacet
-extends      DataFacet
-implements   FacetTypes {
+public class FixLongitudinalAnalysisFacet extends DataFacet implements FacetTypes {
+
+    private static final long serialVersionUID = 1L;
 
     /** House log. */
-    private static Logger log = Logger.getLogger(
-        FixLongitudinalAnalysisFacet.class);
+    private static Logger log = Logger.getLogger(FixLongitudinalAnalysisFacet.class);
+
+    private int periodIndex;
+
+    private int columnId;
 
     /** Trivial Constructor. */
     public FixLongitudinalAnalysisFacet() {
     }
 
+    public FixLongitudinalAnalysisFacet(final int facetIndex, final int periodIndex, final int columnId, final String name, final String description) {
+        super(facetIndex, name, description, ComputeType.ADVANCE, null, null);
 
-    public FixLongitudinalAnalysisFacet(
-        int ndx,
-        String name,
-        String description)
-    {
-        super(
-            ndx,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
+        this.periodIndex = periodIndex;
+        this.columnId = columnId;
     }
 
-
     /**
      * Returns the data this facet requires.
      *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
+     * @param artifact
+     *            the owner artifact.
+     * @param context
+     *            the CallContext.
      *
      * @return the data as KMIndex.
      */
     @Override
-    public Object getData(Artifact artifact, CallContext context) {
+    public Object getData(final Artifact artifact, final CallContext context) {
         log.debug("FixLongitudinalAnalysisFacet.getData");
 
         if (artifact instanceof D4EArtifact) {
-            D4EArtifact flys = (D4EArtifact)artifact;
+            final D4EArtifact flys = (D4EArtifact) artifact;
 
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
+            final CalculationResult res = (CalculationResult) flys.compute(context, ComputeType.ADVANCE, false);
 
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
+            final FixAnalysisResult result = (FixAnalysisResult) res.getData();
 
-            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
-            if (kmPeriods == null) {
-                log.warn("No analysis periods found.");
+            final AnalysisPeriodEventResults eventResults = result.getAnalysisEventResults();
+            final FixResultColumns eventResult = eventResults.getEventResults(this.periodIndex);
+            if (eventResult == null) {
+                log.error("No event result for period: " + this.periodIndex);
                 return null;
             }
-            int periodNdx = index >> 8;
-            int qwdNdx = index & 255;
-            KMIndex<QWD> resPeriods =
-                    new KMIndex<QWD>();
-            for (KMIndex.Entry<AnalysisPeriod[]> entry: kmPeriods) {
-                AnalysisPeriod ap = entry.getValue()[periodNdx];
-                QWD[] qwds = ap.qwds;
-                for(int i = 0; i < qwds.length; i++) {
-                    if(qwds[i].getIndex() == qwdNdx) {
-                        resPeriods.add(entry.getKm(), qwds[i]);
-                    }
-                }
+
+            final FixResultColumn event = eventResult.getColumn(this.columnId);
+            if (event == null) {
+                log.error("Missing event with columnId: " + this.columnId);
+                return null;
             }
 
-
-            return resPeriods;
+            return event.getQWDs();
         }
-        else {
-            log.warn("Artifact is no instance of D4EArtifact.");
-            return null;
-        }
+
+        log.warn("Artifact is no instance of D4EArtifact.");
+        return null;
     }
 
-
     /**
      * Create a deep copy of this Facet.
+     *
      * @return a deep copy.
      */
     @Override
     public FixLongitudinalAnalysisFacet deepCopy() {
-        FixLongitudinalAnalysisFacet copy = new FixLongitudinalAnalysisFacet();
+        final FixLongitudinalAnalysisFacet copy = new FixLongitudinalAnalysisFacet();
         copy.set(this);
+        copy.periodIndex = this.periodIndex;
+        copy.columnId = this.columnId;
         return copy;
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalReferenceFacet.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixLongitudinalReferenceFacet.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,113 +8,84 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import org.apache.log4j.Logger;
 import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.CallContext;
-
 import org.dive4elements.river.artifacts.D4EArtifact;
-
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.DataFacet;
 import org.dive4elements.river.artifacts.model.FacetTypes;
-
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 
-import org.dive4elements.river.utils.KMIndex;
-
-import org.apache.log4j.Logger;
-
-
 /**
  * Facet to show average W values for Q sectors.
  *
  * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
  */
-public class FixLongitudinalReferenceFacet
-extends      DataFacet
-implements   FacetTypes {
+public class FixLongitudinalReferenceFacet extends DataFacet implements FacetTypes {
+
+    private static final long serialVersionUID = 1L;
 
     /** House log. */
-    private static Logger log = Logger.getLogger(
-        FixLongitudinalReferenceFacet.class);
+    private static Logger log = Logger.getLogger(FixLongitudinalReferenceFacet.class);
+
+    private int columnId;
 
     /** Trivial Constructor. */
     public FixLongitudinalReferenceFacet() {
     }
 
+    public FixLongitudinalReferenceFacet(final int facetIndex, final int columnId, final String name, final String description) {
+        super(facetIndex, name, description, ComputeType.ADVANCE, null, null);
 
-    public FixLongitudinalReferenceFacet(
-        int ndx,
-        String name,
-        String description)
-    {
-        super(
-            ndx,
-            name,
-            description,
-            ComputeType.ADVANCE,
-            null,
-            null);
+        this.columnId = columnId;
     }
 
-
     /**
      * Returns the data this facet requires.
      *
-     * @param artifact the owner artifact.
-     * @param context  the CallContext.
+     * @param artifact
+     *            the owner artifact.
+     * @param context
+     *            the CallContext.
      *
      * @return the data as KMIndex.
      */
     @Override
-    public Object getData(Artifact artifact, CallContext context) {
+    public Object getData(final Artifact artifact, final CallContext context) {
         log.debug("FixLongitudinalReferenceFacet.getData");
 
         if (artifact instanceof D4EArtifact) {
-            D4EArtifact flys = (D4EArtifact)artifact;
+            final D4EArtifact flys = (D4EArtifact) artifact;
 
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
+            final CalculationResult res = (CalculationResult) flys.compute(context, ComputeType.ADVANCE, false);
 
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
+            final FixAnalysisResult result = (FixAnalysisResult) res.getData();
 
-            KMIndex<QWD []> kmReference = result.getFixings();
-
-            if (kmReference == null) {
-                log.warn("No references found.");
+            final FixResultColumns resultColumns = result.getFixResultColumns();
+            final FixResultColumn event = resultColumns.getColumn(this.columnId);
+            if (event == null) {
+                log.error("Missing event witrh columnId: " + this.columnId);
                 return null;
             }
 
-            int qwdNdx = index & 255;
-            
-            final KMIndex<QWD> resReference = new KMIndex<>();
-            
-            for (KMIndex.Entry<QWD[]> entry: kmReference) {
-                QWD[] qwds = entry.getValue();
-                for(int i = 0; i < qwds.length; i++) {
-                    if(qwds[i].getIndex() == qwdNdx && !qwds[i].isOutlier() ) {
-                        resReference.add(entry.getKm(), qwds[i]);
-                    }
-                }
-            }
+            return event.getQWDs();
+        }
 
-            return resReference;
-        }
-        
         log.warn("Artifact is no instance of D4EArtifact.");
         return null;
     }
 
     /**
      * Create a deep copy of this Facet.
+     *
      * @return a deep copy.
      */
     @Override
     public FixLongitudinalReferenceFacet deepCopy() {
-        FixLongitudinalReferenceFacet copy =
-            new FixLongitudinalReferenceFacet();
+        final FixLongitudinalReferenceFacet copy = new FixLongitudinalReferenceFacet();
         copy.set(this);
+        copy.columnId = this.columnId;
         return copy;
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculation.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculation.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,41 +8,33 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import java.util.List;
+
 import org.dive4elements.river.artifacts.access.FixRealizingAccess;
-
 import org.dive4elements.river.artifacts.math.fitting.Function;
-
 import org.dive4elements.river.artifacts.model.CalculationResult;
-import org.dive4elements.river.artifacts.model.FixingsOverview;
+import org.dive4elements.river.artifacts.model.Parameters;
 import org.dive4elements.river.artifacts.model.RiverFactory;
 import org.dive4elements.river.artifacts.model.Segment;
 import org.dive4elements.river.artifacts.model.WQKms;
-import org.dive4elements.river.artifacts.model.Parameters;
-
 import org.dive4elements.river.model.River;
 
-import java.util.List;
-
-import org.apache.log4j.Logger;
-
 /** Calculation for FixRealize (german: ausgel. WSPL). */
-public class FixRealizingCalculation
-extends      FixCalculation
-{
-    private static Logger log =
-        Logger.getLogger(FixRealizingCalculation.class);
+public class FixRealizingCalculation extends FixCalculation {
+    private static final long serialVersionUID = 1L;
 
-    protected boolean       isQ;
+    protected boolean isQ;
+
     protected List<Segment> segments;
 
     public FixRealizingCalculation() {
     }
 
-    public FixRealizingCalculation(FixRealizingAccess access) {
+    public FixRealizingCalculation(final FixRealizingAccess access) {
         super(access);
 
-        Boolean       isQ      = access.isQ();
-        List<Segment> segments = access.getSegments();
+        final Boolean isQ = access.isQ();
+        final List<Segment> segments = access.getSegments();
 
         if (isQ == null) {
             addProblem("fix.realize.missing.is.q");
@@ -52,14 +44,14 @@
             addProblem("fix.realize.missing.segments");
         }
 
-        River r = RiverFactory.getRiver(river);
+        final River r = RiverFactory.getRiver(this.river);
 
         if (r == null) {
             addProblem("fix.no.such.river");
         }
 
         if (!hasProblems()) {
-            this.isQ      = isQ;
+            this.isQ = isQ;
             this.segments = segments;
 
             // Convert from W to Q
@@ -68,39 +60,35 @@
     }
 
     @Override
-    protected CalculationResult innerCalculate(
-        FixingsOverview overview,
-        Function        func
-    ) {
-        ColumnCache cc = new ColumnCache();
-        FitResult fitResult = doFitting(overview, cc, func);
+    protected CalculationResult innerCalculate(final FixingsOverview overview, final Function func) {
+        final ColumnCache cc = new ColumnCache();
+        final FitResult fitResult = doFitting(overview, cc, func);
 
         if (fitResult == null) {
             return new CalculationResult(this);
         }
 
-        Segment segment = segments.get(0);
-        int numResults = segment.numValues();
+        Segment segment = this.segments.get(0);
+        final int numResults = segment.numValues();
 
-        WQKms [] results = new WQKms[numResults];
+        final WQKms[] results = new WQKms[numResults];
         for (int i = 0; i < results.length; ++i) {
             results[i] = new WQKms();
         }
 
-        Parameters parameters = fitResult.getParameters();
+        final Parameters parameters = fitResult.getParameters();
 
-        int kmIndex = parameters.columnIndex("km");
-        int [] parameterIndices =
-            parameters.columnIndices(func.getParameterNames());
+        final int kmIndex = parameters.columnIndex("km");
+        final int[] parameterIndices = parameters.columnIndices(func.getParameterNames());
 
-        double [] parameterValues = new double[parameterIndices.length];
+        final double[] parameterValues = new double[parameterIndices.length];
 
         for (int row = 0, R = parameters.size(); row < R; ++row) {
-            double km = parameters.get(row, kmIndex);
+            final double km = parameters.get(row, kmIndex);
 
             if (!segment.inside(km)) {
                 Segment nextSeg = null;
-                for (Segment seg: segments) {
+                for (final Segment seg : this.segments) {
                     if (seg.inside(km)) {
                         nextSeg = seg;
                         break;
@@ -115,18 +103,16 @@
 
             parameters.get(row, parameterIndices, parameterValues);
 
-            org.dive4elements.river.artifacts.math.Function instance =
-                func.instantiate(parameterValues);
+            final org.dive4elements.river.artifacts.math.Function instance = func.instantiate(parameterValues);
 
-            double [] values = segment.getValues();
+            final double[] values = segment.getValues();
             for (int i = 0; i < numResults; ++i) {
-                double q = values[i];
-                double w = instance.value(q);
+                final double q = values[i];
+                final double w = instance.value(q);
 
                 if (Double.isNaN(w)) {
                     addProblem(km, "fix.cannot.calculate.function", q);
-                }
-                else {
+                } else {
                     results[i].add(w, q, km);
                 }
             }
@@ -136,27 +122,26 @@
         for (int i = 0; i < results.length; ++i)
             results[i].setName(createName(i));
 
-        final FixRealizingResult frr = new FixRealizingResult( parameters, fitResult.getFixings(), results);
+        final FixRealizingResult frr = new FixRealizingResult(parameters, fitResult.getResultColumns(), results);
 
         return new CalculationResult(frr, this);
     }
 
     // TODO: issue1109/2
-    protected String createName(int index) {
+    protected String createName(final int index) {
         // TODO: i18n
-        StringBuilder sb = new StringBuilder(isQ ? "Q" : "W");
+        final StringBuilder sb = new StringBuilder(this.isQ ? "Q" : "W");
         sb.append(" benutzerdefiniert (");
-        for (int i = 0, N = segments.size(); i < N; ++i) {
+        for (int i = 0, N = this.segments.size(); i < N; ++i) {
             if (i > 0) {
                 sb.append("; ");
             }
-            Segment segment = segments.get(i);
-            double [] backup = segment.getBackup();
-            double [] values = segment.getValues();
+            final Segment segment = this.segments.get(i);
+            final double[] backup = segment.getBackup();
+            final double[] values = segment.getValues();
             sb.append((backup != null ? backup : values)[index]);
         }
         sb.append(')');
         return sb.toString();
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculationExtended.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculationExtended.java	Thu Aug 16 16:27:53 2018 +0200
@@ -16,8 +16,6 @@
 import java.util.TreeSet;
 
 import org.dive4elements.river.artifacts.access.FixRealizingAccess;
-import org.dive4elements.river.artifacts.model.FixingsOverview;
-import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
 
 /**
  * REMARK: this inheritance is only needed, beause changing the orignal calculation will probably break the
@@ -40,8 +38,8 @@
 
         final Calendar cal = Calendar.getInstance();
 
-        final List<Column> columns = getEventColumns(overview, cc);
-        for (final Column column : columns) {
+        final List<FixingColumnWithData> columns = getEventColumns(overview, cc);
+        for (final FixingColumnWithData column : columns) {
             final Date date = column.getDate();
             cal.setTime(date);
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingResult.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingResult.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,38 +8,27 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import org.dive4elements.river.artifacts.model.Parameters;
 import org.dive4elements.river.artifacts.model.WQKms;
-
-import org.dive4elements.river.artifacts.model.Parameters;
 import org.dive4elements.river.artifacts.model.WQKmsResult;
 
-import org.dive4elements.river.utils.KMIndex;
-
 /** Result of a FixRealizing Calculation. */
-public class FixRealizingResult
-extends      FixResult
-implements   WQKmsResult
-{
-    public WQKms [] wqkms;
+public class FixRealizingResult extends FixResult implements WQKmsResult {
+    private static final long serialVersionUID = 1L;
+
+    public WQKms[] wqkms;
 
     public FixRealizingResult() {
     }
 
-    public FixRealizingResult(
-        Parameters      parameters,
-        KMIndex<QWD []> fixings,
-        WQKms []        wqkms
-    ) {
-        super(parameters, fixings);
+    public FixRealizingResult(final Parameters parameters, final FixResultColumns fixResultColumns, final WQKms[] wqkms) {
+        super(parameters, fixResultColumns);
+
         this.wqkms = wqkms;
     }
 
     @Override
-    public WQKms [] getWQKms() {
-        return wqkms;
-    }
-
-    public void setWQKms(WQKms [] wqkms) {
-        this.wqkms = wqkms;
+    public WQKms[] getWQKms() {
+        return this.wqkms;
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixReferenceEventsFacet.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixReferenceEventsFacet.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,6 +8,7 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import org.apache.commons.lang.math.DoubleRange;
 import org.apache.log4j.Logger;
 import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.CallContext;
@@ -15,24 +16,29 @@
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
-import org.dive4elements.river.utils.KMIndex;
 
 /**
  * Facet to show W values for Q values at km for a date.
  *
  * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
  */
-public class FixReferenceEventsFacet extends FixingsFacet implements FacetTypes {
+public class FixReferenceEventsFacet extends FixingsEventFacet implements FacetTypes {
+
+    private static final long serialVersionUID = 1L;
 
     /** House log. */
     private static Logger log = Logger.getLogger(FixReferenceEventsFacet.class);
 
+    private int columnId;
+
     /** Trivial Constructor. */
     public FixReferenceEventsFacet() {
     }
 
-    public FixReferenceEventsFacet(final int index, final String name, final String description) {
-        super(index, name, description, ComputeType.ADVANCE, null, null);
+    public FixReferenceEventsFacet(final int facetIndex, final int columnId, final String name, final String description, final DoubleRange stationRange) {
+        super(facetIndex, name, description, ComputeType.ADVANCE, null, null, stationRange);
+
+        this.columnId = columnId;
     }
 
     /**
@@ -64,23 +70,14 @@
         if (log.isDebugEnabled())
             log.debug("current km in FRE: " + currentKm);
 
-        /* first search in referenced */
-        final KMIndex<QWD[]> referenced = result.getFixings();
-        return find(referenced, currentKm);
-    }
-
-    private QWD find(final KMIndex<QWD[]> kmQWs, final double currentKm) {
-
-        final KMIndex.Entry<QWD[]> kmQWsEntry = kmQWs.binarySearch(currentKm);
-        if (kmQWsEntry != null) {
-            final int ndx = this.index & 255;
-            for (final QWD qwd : kmQWsEntry.getValue()) {
-                if (qwd.getIndex() == ndx)
-                    return qwd;
-            }
+        final FixResultColumns resultColumns = result.getFixResultColumns();
+        final FixResultColumn event = resultColumns.getColumn(this.columnId);
+        if (event == null) {
+            log.error("Missing event with columnId: " + this.columnId);
+            return null;
         }
 
-        return null;
+        return event.getQWD(currentKm);
     }
 
     /**
@@ -92,6 +89,7 @@
     public FixReferenceEventsFacet deepCopy() {
         final FixReferenceEventsFacet copy = new FixReferenceEventsFacet();
         copy.set(this);
+        copy.columnId = this.columnId;
         return copy;
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResult.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResult.java	Thu Aug 16 16:27:53 2018 +0200
@@ -9,78 +9,30 @@
 package org.dive4elements.river.artifacts.model.fixings;
 
 import java.io.Serializable;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
 
 import org.dive4elements.river.artifacts.model.Parameters;
-import org.dive4elements.river.utils.KMIndex;
 
 public class FixResult implements Serializable
 {
     private static final long serialVersionUID = 1L;
 
-    protected Parameters      parameters;
-    protected KMIndex<QWD []> fixings;
+    private Parameters parameters;
+
+    private FixResultColumns fixResultColumns;
 
     public FixResult() {
     }
 
-    public FixResult( final Parameters parameters, final KMIndex<QWD []> fixings ) {
+    public FixResult(final Parameters parameters, final FixResultColumns fixResultColumns) {
         this.parameters = parameters;
-        this.fixings = fixings;
-    }
-
-    public KMIndex<QWD []> getFixings() {
-        return fixings;
-    }
-
-    public void makeEventsDatesUnique() {
-        final DateUniqueMaker dum = new DateUniqueMaker();
-        
-        for (KMIndex.Entry<QWD []> entry: fixings) {
-            for (QWD ref: entry.getValue())
-                dum.makeUnique(ref);
-        }
-    }
-
-    private Collection<Integer> getEventsIndices() {
-        final TreeMap<Date, Integer> dates = new TreeMap<>();
-        
-        for (KMIndex.Entry<QWD []> entry: fixings) {
-            for (QWD value: entry.getValue())
-                dates.put(value.date, value.index);
-        }
-        
-        return dates.values();
-    }
-
-    public void remapEventIndicesToRank() {
-        final RankRemapper remapper = new RankRemapper();
-        for (Integer idx: getEventsIndices())
-            remapper.toMap(idx);
-
-        for (KMIndex.Entry<QWD []> entry: fixings) {
-            for (QWD value: entry.getValue())
-                remapper.remap(value);
-        }
-    }
-
-    public Collection<Date> getReferenceEventsDates() {
-        
-        final Set<Date> dates = new TreeSet<>();
-        
-        for (final KMIndex.Entry<QWD []> entry: fixings) {
-            for (final QWD qwd: entry.getValue())
-                dates.add(qwd.date);
-        }
-        
-        return dates;
+        this.fixResultColumns = fixResultColumns;
     }
 
     public Parameters getParameters() {
-        return parameters;
+        return this.parameters;
+    }
+
+    public FixResultColumns getFixResultColumns() {
+        return this.fixResultColumns;
     }
 }
\ 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/model/fixings/FixResultColumn.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,112 @@
+/** 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.model.fixings;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.Date;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.math.DoubleRange;
+import org.dive4elements.river.utils.KMIndex;
+
+/**
+ * Result data is organized by their original fixing-column, represented by this class.
+ *
+ * @author Gernot Belger
+ */
+public class FixResultColumn implements Serializable {
+
+    public static class DateComparator implements Comparator<FixResultColumn> {
+        @Override
+        public int compare(final FixResultColumn o1, final FixResultColumn o2) {
+
+            final Date date1 = o1.getDate();
+            final Date date2 = o2.getDate();
+            return date1.compareTo(date2);
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+
+    /** We know the columnId is unique within the database, so this takes the place here as primary key */
+    private int columnId;
+
+    private final KMIndex<QWD> data = new KMIndex<>();
+
+    private Date date;
+
+    private DoubleRange stationRange;
+
+    public FixResultColumn() {
+    }
+
+    public FixResultColumn(final int columnId, final Date date, final DoubleRange stationRange) {
+        this.columnId = columnId;
+        this.date = date;
+        this.stationRange = stationRange;
+    }
+
+    public int getColumnId() {
+        return this.columnId;
+    }
+
+    public DoubleRange getStationRange() {
+        return this.stationRange;
+    }
+
+    public Date getDate() {
+        return this.date;
+    }
+
+    public void addQWD(final double km, final QWD qwd) {
+        this.data.add(km, qwd);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+
+        if (obj == null)
+            return false;
+
+        if (obj == this)
+            return true;
+
+        if (obj.getClass() != getClass())
+            return false;
+
+        final FixResultColumn rhs = (FixResultColumn) obj;
+        return new EqualsBuilder() //
+                .append(this.columnId, rhs.columnId) //
+                .isEquals();
+    }
+
+    @Override
+    public int hashCode() {
+
+        return new HashCodeBuilder() //
+                .append(this.columnId) //
+                .toHashCode();
+    }
+
+    public QWD getQWD(final double currentKm) {
+
+        final KMIndex.Entry<QWD> entry = this.data.binarySearch(currentKm);
+        if (entry == null)
+            return null;
+
+        return entry.getValue();
+    }
+
+    public KMIndex<QWD> getQWDs() {
+        return this.data;
+    }
+}
\ 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/model/fixings/FixResultColumns.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,65 @@
+/** 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.model.fixings;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Gernot Belger
+ */
+public class FixResultColumns implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private final Map<Integer, FixResultColumn> resultColumns = new HashMap<>();
+
+    public FixResultColumns() {
+    }
+
+    public Collection<FixResultColumn> getSortedColumns() {
+        final Collection<FixResultColumn> values = this.resultColumns.values();
+
+        final List<FixResultColumn> sorted = new ArrayList<>(values);
+
+        Collections.sort(sorted, new FixResultColumn.DateComparator());
+
+        return sorted;
+    }
+
+    public FixResultColumn getColumn(final int columnId) {
+        return this.resultColumns.get(columnId);
+    }
+
+    public void addQWD(final FixingColumnWithData event, final double km, final QWD qwd) {
+        final FixResultColumn columnResult = getOrCreateColumResult(event);
+        columnResult.addQWD(km, qwd);
+    }
+
+    public void sortAll() {
+        for (final FixResultColumn column : this.resultColumns.values())
+            column.getQWDs().sort();
+    }
+
+    private FixResultColumn getOrCreateColumResult(final FixingColumnWithData event) {
+
+        final FixResultColumn existingColumn = this.resultColumns.get(event.getId());
+        if (existingColumn != null)
+            return existingColumn;
+
+        final FixResultColumn newColumn = new FixResultColumn(event.getId(), event.getDate(), event.getStationRange());
+        this.resultColumns.put(event.getId(), newColumn);
+        return newColumn;
+    }
+}
\ 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/model/fixings/Fixing.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,104 @@
+/** 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.model.fixings;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import org.dive4elements.river.artifacts.model.GaugeFinder;
+import org.dive4elements.river.artifacts.model.Range;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.FixColumn;
+
+public class Fixing implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final Comparator<FixingColumn> DATE_CMP = new Comparator<FixingColumn>() {
+        @Override
+        public int compare(final FixingColumn a, final FixingColumn b) {
+            return a.getStartTime().compareTo(b.getStartTime());
+        }
+    };
+
+    private int wstId;
+
+    private String description;
+
+    private List<FixingColumn> columns;
+
+    public Fixing() {
+    }
+
+    public int getId() {
+        return this.wstId;
+    }
+
+    public String getDescription() {
+        return this.description;
+    }
+
+    public Fixing(final int wstId, final String description) {
+        this.wstId = wstId;
+        this.description = description;
+        this.columns = new ArrayList<>();
+    }
+
+    public void allColumnIds(final List<Integer> cIds) {
+        for (final FixingColumn column : this.columns)
+            cIds.add(column.getId());
+    }
+
+    public void loadColumns(final FixColumnLoader loader) {
+        final List<FixColumn> fcs = loader.get(this.wstId);
+        if (fcs == null) {
+            FixingsOverview.log.warn("No columns for wst " + this.wstId);
+            return;
+        }
+        for (final FixColumn fc : fcs) {
+            this.columns.add(new FixingColumn(this, fc.columnId, fc.startTime, fc.name));
+        }
+    }
+
+    public void loadColumnsKmRange(final KMRangeLoader loader) {
+        for (final FixingColumn column : this.columns) {
+            column.loadKmRange(loader);
+        }
+    }
+
+    public void adjustExtent(final Range extent) {
+        for (final FixingColumn column : this.columns) {
+            extent.extend(column);
+        }
+    }
+
+    public void loadColumnsQRanges(final ColumnQRangeLoader loader, final GaugeFinder gaugeFinder) {
+        for (final FixingColumn column : this.columns) {
+            column.loadQRanges(loader, gaugeFinder);
+        }
+    }
+
+    /**
+     * @param allColumns[out]
+     *            Columns will be put here.
+     * @param range
+     *            can be null.
+     * @param filter
+     *            filter to apply.
+     */
+    public void addAllColumns(final List<FixingColumn> allColumns, final Range range, final FixingColumnFilter filter) {
+        for (final FixingColumn column : this.columns) {
+            if ((range == null || column.hasSectorsInRange(range)) && (filter == null || filter.accept(column))) {
+                allColumns.add(column);
+            }
+        }
+    }
+}
\ 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/model/fixings/FixingColumn.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,152 @@
+/** 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.model.fixings;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.dive4elements.river.artifacts.model.GaugeFinder;
+import org.dive4elements.river.artifacts.model.GaugeRange;
+import org.dive4elements.river.artifacts.model.Range;
+
+public class FixingColumn extends Range {
+
+    private static final long serialVersionUID = 1L;
+
+    private Fixing fixing;
+
+    private int columnId;
+
+    private Date startTime;
+
+    private String name;
+
+    private List<SectorRange> sectors;
+
+    public FixingColumn() {
+    }
+
+    public FixingColumn(final Fixing fixing, final int columnId, final Date startTime, final String name) {
+        this.fixing = fixing;
+        this.columnId = columnId;
+        this.startTime = startTime;
+        this.name = name;
+
+        this.sectors = new ArrayList<>();
+    }
+
+    public int getId() {
+        return this.columnId;
+    }
+
+    public Fixing getFixing() {
+        return this.fixing;
+    }
+
+    public Date getStartTime() {
+        return this.startTime;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getDescription() {
+        return this.fixing.getDescription() + "/" + this.name;
+    }
+
+    public List<SectorRange> getSectors() {
+        return this.sectors;
+    }
+
+    public boolean hasSectorsInRange(final Range range) {
+        for (final SectorRange sector : this.sectors) {
+            if (sector.intersects(range)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public List<SectorRange> getSectors(final Range range) {
+
+        final List<SectorRange> result = new ArrayList<>(this.sectors.size());
+
+        for (final SectorRange src : this.sectors) {
+            final SectorRange dst = new SectorRange(src);
+            if (range == null || dst.clip(range)) {
+                result.add(dst);
+            }
+        }
+
+        return result;
+    }
+
+    public int findQSector(final double km) {
+        for (final SectorRange sector : this.sectors) {
+            if (sector.inside(km)) {
+                return sector.getSector();
+            }
+        }
+        return -1;
+    }
+
+    public void buildSectors(final GaugeFinder gaugeFinder, final List<QRange> qRanges) {
+        for (final QRange qRange : qRanges) {
+            for (final GaugeRange gRange : gaugeFinder.getGauges()) {
+                final SectorRange sector = new SectorRange(qRange);
+                if (!sector.clip(gRange)) {
+                    continue;
+                }
+                sector.setSector(gRange.classify(qRange.q));
+
+                if (this.sectors.isEmpty() || !this.sectors.get(this.sectors.size() - 1).enlarge(sector)) {
+                    this.sectors.add(sector);
+                }
+            } // for all gauges
+        } // for all Q ranges
+    }
+
+    public void loadKmRange(final KMRangeLoader loader) {
+
+        final double[] range = loader.get(this.columnId);
+
+        if (range == null) {
+            FixingsOverview.log.warn("No km range for column " + this.columnId + ".");
+            return;
+        }
+        this.setStart(range[0]);
+        this.setEnd(range[1]);
+    }
+
+    public void loadQRanges(final ColumnQRangeLoader loader, final GaugeFinder gaugeFinder) {
+        final List<double[]> qrs = loader.get(this.columnId);
+        if (qrs == null) {
+            FixingsOverview.log.warn("No q ranges found for column " + this.columnId);
+            return;
+        }
+
+        final List<QRange> qRanges = new ArrayList<>(qrs.size());
+
+        for (final double[] qr : qrs) {
+            final double q = qr[0];
+            final double start = qr[1];
+            final double end = qr[2];
+
+            final QRange qRange = new QRange(start, end, q);
+            if (qRange.clip(this)) {
+                qRanges.add(qRange);
+            }
+        }
+
+        buildSectors(gaugeFinder, qRanges);
+    }
+}
\ 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/model/fixings/FixingColumnData.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,65 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.model.fixings;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.dive4elements.river.artifacts.math.Linear;
+import org.dive4elements.river.artifacts.model.QRangeTree;
+
+public class FixingColumnData implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private double[] kms;
+    private double[] ws;
+
+    private QRangeTree qs;
+
+    public FixingColumnData() {
+    }
+
+    public FixingColumnData(final double[] kms, final double[] ws, final QRangeTree qs) {
+        this.kms = kms;
+        this.ws = ws;
+        this.qs = qs;
+    }
+
+    public boolean getW(final double km, final double[] w) {
+        return getW(km, w, 0);
+    }
+
+    public boolean getW(final double km, final double[] w, final int index) {
+
+        if (this.kms.length == 0 || km < this.kms[0] || km > this.kms[this.kms.length - 1]) {
+            w[index] = Double.NaN;
+            return true;
+        }
+
+        int idx = Arrays.binarySearch(this.kms, km);
+
+        if (idx >= 0) {
+            w[index] = this.ws[idx];
+            return true;
+        }
+
+        idx = -idx - 1;
+
+        w[index] = Linear.linear(km, this.kms[idx - 1], this.kms[idx], this.ws[idx - 1], this.ws[idx]);
+        return false;
+    }
+
+    public double getQ(final double km) {
+        return this.qs.findQ(km);
+    }
+
+    public QRangeTree getQRanges() {
+        return this.qs;
+    }
+}
\ 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/model/fixings/FixingColumnFilter.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,14 @@
+/** 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.model.fixings;
+
+public interface FixingColumnFilter {
+    boolean accept(FixingColumn column);
+}
\ 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/model/fixings/FixingColumnWithData.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,77 @@
+/** 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.model.fixings;
+
+import java.util.Date;
+
+import org.apache.commons.lang.math.DoubleRange;
+
+/**
+ * Helper class to bundle the meta information of a column
+ * and the real data.
+ */
+class FixingColumnWithData {
+
+    private FixingColumn meta;
+
+    private FixingColumnData data;
+
+    private int index;
+
+    public FixingColumnWithData() {
+    }
+
+    public FixingColumnWithData(final FixingColumn meta, final FixingColumnData data, final int index) {
+        this.meta = meta;
+        this.data = data;
+        this.index = index;
+    }
+
+    public DoubleRange getStationRange() {
+        return new DoubleRange(this.meta.getStart(), this.meta.getEnd());
+    }
+
+    public Date getDate() {
+        return this.meta.getStartTime();
+    }
+
+    public String getDescription() {
+        return this.meta.getDescription();
+    }
+
+    public int getIndex() {
+        return this.index;
+    }
+
+    public int getId() {
+        return this.meta.getId();
+    }
+
+    public double getQ(final double km) {
+        return this.data.getQ(km);
+    }
+
+    public boolean getW(final double km, final double[] ws, final int arrayIndex) {
+        return this.data.getW(km, ws, arrayIndex);
+    }
+
+    public boolean getQW(final double km, final double[] qs, final double[] ws, final int arrayIndex) {
+        qs[this.index] = this.data.getQ(km);
+        return this.data.getW(km, ws, arrayIndex);
+    }
+
+    public boolean getQW(final double km, final double[] wq) {
+        this.data.getW(km, wq, 0);
+        if (Double.isNaN(wq[0]))
+            return false;
+        wq[1] = this.data.getQ(km);
+        return !Double.isNaN(wq[1]);
+    }
+}
\ 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/model/fixings/FixingsColumnFactory.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,137 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.artifacts.model.QRangeTree;
+import org.dive4elements.river.backend.SessionHolder;
+
+import java.util.List;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.hibernate.Session;
+import org.hibernate.SQLQuery;
+
+import org.hibernate.type.StandardBasicTypes;
+
+import org.apache.log4j.Logger;
+
+public class FixingsColumnFactory
+{
+    private static Logger log = Logger.getLogger(FixingsColumnFactory.class);
+
+    public static final String CACHE_NAME = "fixings-columns";
+
+    public static final String SQL_COLUMN_WS =
+        "SELECT wcv.position AS km, wcv.w AS w " +
+        "FROM wst_column_values wcv " +
+        "WHERE wst_column_id = :column_id " +
+        "ORDER by wcv.position";
+
+    public static final String SQL_COLUMN_QS =
+        "SELECT wqr.q AS q, r.a AS a, r.b AS b " +
+        "FROM wst_column_q_ranges wcqr " +
+        "JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id = wqr.id " +
+        "JOIN ranges r         ON wqr.range_id        = r.id " +
+        "WHERE wcqr.wst_column_id = :column_id ORDER by r.a";
+
+    public static final FixingsColumnFactory INSTANCE =
+        new FixingsColumnFactory();
+
+    private FixingsColumnFactory() {
+    }
+
+    public static FixingsColumnFactory getInstance() {
+        return INSTANCE;
+    }
+
+    public FixingColumnData getColumnData(FixingColumn column) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("FixingsColumnFactory.getColumnData");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache unconfigured.");
+            }
+            return getUncached(column);
+        }
+
+        Integer cacheKey = Integer.valueOf(column.getId());
+        Element element  = cache.get(cacheKey);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Column " + cacheKey + " found in cache.");
+            }
+            return (FixingColumnData)element.getValue();
+        }
+        else {
+            FixingColumnData result = getUncached(column);
+            if (result != null) {
+                if (debug) {
+                    log.debug("Store column " + cacheKey + " into cache.");
+                }
+                cache.put(new Element(cacheKey, result));
+            }
+            return result;
+        }
+    }
+
+    protected FixingColumnData getUncached(FixingColumn column) {
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_COLUMN_WS)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("w",  StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("column_id", column.getId());
+
+        List<Object []> results = sqlQuery.list();
+
+        if (results.isEmpty()) {
+            return null;
+        }
+
+        double [] kms = new double[results.size()];
+        double [] ws  = new double[kms.length];
+
+        for (int i = 0; i < kms.length; ++i) {
+            Object [] row = results.get(i);
+            kms[i] = ((Double)row[0]).doubleValue();
+            ws [i] = ((Double)row[1]).doubleValue();
+        }
+
+        sqlQuery = session.createSQLQuery(SQL_COLUMN_QS)
+            .addScalar("q", StandardBasicTypes.DOUBLE)
+            .addScalar("a", StandardBasicTypes.DOUBLE)
+            .addScalar("b", StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("column_id", column.getId());
+
+        results = sqlQuery.list();
+
+        if (results.isEmpty()) {
+            return null;
+        }
+
+        QRangeTree qs = new QRangeTree(
+            results, QRangeTree.WITHOUT_COLUMN, 0, results.size());
+
+        return new FixingColumnData(kms, ws, qs);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsEventFacet.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,60 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.apache.commons.lang.math.DoubleRange;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Facet to access the current Km from the context safely
+ *
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+abstract class FixingsEventFacet extends FixingsFacet {
+
+    private static final long serialVersionUID = 1L;
+
+    private DoubleRange stationRange;
+
+    public FixingsEventFacet() {
+    }
+
+    public FixingsEventFacet(final int facetIndex, final String name, final String description, final ComputeType type, final String hash, final String stateId,
+            final DoubleRange stationRange) {
+        super(facetIndex, name, description, type, hash, stateId);
+
+        this.stationRange = stationRange;
+    }
+
+    @Override
+    public Node toXML(final Document doc) {
+
+        final ElementCreator ec = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        final Element e = (Element) super.toXML(doc);
+        ec.addAttr(e, "startKm", Double.toString(this.stationRange.getMinimumDouble()), true);
+        ec.addAttr(e, "endKm", Double.toString(this.stationRange.getMaximumDouble()), true);
+        return e;
+    }
+
+    @Override
+    public void set(final Facet other) {
+        super.set(other);
+
+        final FixingsEventFacet o = (FixingsEventFacet) other;
+        // DoubleRange is immutable, so we can copy the reference here
+        o.stationRange = this.stationRange;
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsFacet.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsFacet.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,35 +8,27 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import static org.dive4elements.river.exports.injector.InjectorConstants.CURRENT_KM;
+
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.model.DataFacet;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 
-import static org.dive4elements.river.exports.injector.InjectorConstants.CURRENT_KM;
 /**
  * Facet to access the current Km from the context safely
  *
  * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
  */
-public class FixingsFacet extends DataFacet {
+public abstract class FixingsFacet extends DataFacet {
+
+    private static final long serialVersionUID = 1L;
 
     public static final Double INVALID_KM = Double.valueOf(-1d);
 
     public FixingsFacet() {
     }
 
-    public  FixingsFacet(String name, String description) {
-        super(0, name, description, ComputeType.ADVANCE, null, null);
-    }
-
-    public FixingsFacet(
-            int         index,
-            String      name,
-            String      description,
-            ComputeType type,
-            String      hash,
-            String      stateId
-            ) {
+    public FixingsFacet(final int index, final String name, final String description, final ComputeType type, final String hash, final String stateId) {
         super(index, name, description, type, hash, stateId);
     }
 
@@ -44,17 +36,19 @@
      * Returns the current km from the context.
      * If the context is null or doesn't contain a currentKm
      * then a double value of -1 will be returned.
-     * @param context The CallContext instance
+     *
+     * @param context
+     *            The CallContext instance
      * @return the current km as double
      */
-    protected double getCurrentKm(CallContext context) {
+    protected final double getCurrentKm(final CallContext context) {
         if (context == null) {
             return INVALID_KM;
         }
-        Double dkm = (Double)context.getContextValue(CURRENT_KM);
+        final Double dkm = (Double) context.getContextValue(CURRENT_KM);
         if (dkm == null) {
             return INVALID_KM;
         }
         return dkm.doubleValue();
     }
-}
+}
\ 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/model/fixings/FixingsFilterBuilder.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,251 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.model.fixings;
+
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.river.artifacts.model.Range;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.AndFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.DateFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.DateRangeFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.IdFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.IdsFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.KmFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.NotFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.OrFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.SectorFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.SectorRangeFilter;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class FixingsFilterBuilder
+{
+    private static Logger log = Logger.getLogger(FixingsFilterBuilder.class);
+
+    protected FixingColumnFilter   filter;
+    protected Range    range;
+
+    protected Document document;
+
+    public FixingsFilterBuilder() {
+    }
+
+    public FixingsFilterBuilder(Document document) {
+        this.document = document;
+    }
+
+    public FixingColumnFilter getFilter() {
+        if (filter == null) {
+            filter = buildFilter();
+        }
+        return filter;
+    }
+
+    public Range getRange() {
+        if (range == null) {
+            range = buildRange();
+        }
+        return range;
+    }
+
+    public Document getDocument() {
+        return document;
+    }
+
+    protected Range buildRange() {
+
+        NodeList ranges = document.getElementsByTagName("range");
+
+        if (ranges.getLength() < 1) {
+            return FixingsOverview.FULL_EXTENT;
+        }
+
+        Element range = (Element)ranges.item(0);
+
+        String from = range.getAttribute("from").trim();
+        String to   = range.getAttribute("to"  ).trim();
+
+        double start = -Double.MAX_VALUE;
+        double end   =  Double.MAX_VALUE;
+
+        if (from.length() > 0) {
+            try {
+                start = Double.parseDouble(from);
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("Invalid from value: " + from);
+            }
+        }
+
+        if (to.length() > 0) {
+            try {
+                end = Double.parseDouble(to);
+            }
+            catch (NumberFormatException nfe) {
+                log.warn("Invalid to value: " + to);
+            }
+        }
+
+        if (start > end) {
+            double t = start;
+            start = end;
+            end = t;
+        }
+
+        return new Range(start, end);
+    }
+
+    protected FixingColumnFilter buildFilter() {
+        NodeList filters = document.getElementsByTagName("filter");
+
+        return filters.getLength() < 1
+            ? FixingsOverview.ACCEPT
+            : buildFilter((Element)filters.item(0));
+    }
+
+    protected static FixingColumnFilter buildFilter(Element root) {
+        List<FixingColumnFilter> filters = buildRecursiveFilter(root);
+        switch (filters.size()) {
+            case  0: return FixingsOverview.ACCEPT;
+            case  1: return filters.get(0);
+            default: return new AndFilter(filters);
+        }
+    }
+
+    protected static final Date parseDate(String text) {
+        SimpleDateFormat format =
+            new SimpleDateFormat(FixingsOverview.DATE_FORMAT);
+        return format.parse(text, new ParsePosition(0));
+    }
+
+    protected static List<FixingColumnFilter> buildRecursiveFilter(Element root) {
+        List<FixingColumnFilter> filters = new ArrayList<FixingColumnFilter>();
+
+        NodeList children = root.getChildNodes();
+
+        for (int i = 0, N = children.getLength(); i < N; ++i) {
+            Node child = children.item(i);
+            if (child.getNodeType() != Node.ELEMENT_NODE) {
+                continue;
+            }
+
+            Element element = (Element)child;
+            String name = element.getLocalName();
+
+            if ("and".equals(name)) {
+                filters.add(new AndFilter(buildRecursiveFilter(element)));
+            }
+            else if ("or".equals(name)) {
+                filters.add(new OrFilter(buildRecursiveFilter(element)));
+            }
+            else if ("not".equals(name)) {
+                List<FixingColumnFilter> childrenFilters = buildRecursiveFilter(element);
+                if (!childrenFilters.isEmpty()) {
+                    filters.add(new NotFilter(childrenFilters.get(0)));
+                }
+            }
+            else if ("column".equals(name)) {
+                String cid = element.getAttribute("cid").trim();
+                if (cid.length() > 0) {
+                    try {
+                        filters.add(new IdFilter(Integer.parseInt(cid)));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+            }
+            else if ("columns".equals(name)) {
+                String cidsS = element.getAttribute("cids").trim();
+                String [] parts = cidsS.split("\\s+");
+                List<Integer> ids = new ArrayList<Integer>();
+                for (String part: parts) {
+                    try {
+                        ids.add(Integer.valueOf(part));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+                int [] cids = new int[ids.size()];
+                for (int j = 0; j < cids.length; ++j) {
+                    cids[j] = ids.get(j);
+                }
+                filters.add(new IdsFilter(cids));
+            }
+            else if ("date".equals(name)) {
+                String when = element.getAttribute("when").trim();
+                if (when.length() > 0) {
+                    Date date = parseDate(when);
+                    if (date != null) {
+                        filters.add(new DateFilter(date));
+                    }
+                }
+            }
+            else if ("date-range".equals(name)) {
+                String from = element.getAttribute("from").trim();
+                String to   = element.getAttribute("to"  ).trim();
+                if (from.length() > 0 && to.length() > 0) {
+                    Date start = parseDate(from);
+                    Date end   = parseDate(to);
+                    if (start != null && end != null) {
+                        filters.add(new DateRangeFilter(start, end));
+                    }
+                }
+            }
+            else if ("sector-range".equals(name)) {
+                String from = element.getAttribute("from").trim();
+                String to   = element.getAttribute("to"  ).trim();
+                if (from.length() > 0 && to.length() > 0) {
+                    try {
+                        filters.add(new SectorRangeFilter(
+                            Integer.parseInt(from),
+                            Integer.parseInt(to)));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+            }
+            else if ("sector".equals(name)) {
+                String value = element.getAttribute("value").trim();
+                if (value.length() > 0) {
+                    try {
+                        filters.add(new SectorFilter(Integer.parseInt(value)));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+            }
+            else if ("position".equals(name)) {
+                String km = element.getAttribute("km").trim();
+                if (km.length() > 0) {
+                    try {
+                        filters.add(new KmFilter(Double.parseDouble(km)));
+                    }
+                    catch (NumberFormatException nfe) {
+                        log.warn(nfe);
+                    }
+                }
+            }
+        }
+
+        return filters;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsOverview.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,517 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.model.fixings;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.river.artifacts.model.GaugeFinder;
+import org.dive4elements.river.artifacts.model.GaugeFinderFactory;
+import org.dive4elements.river.artifacts.model.GaugeRange;
+import org.dive4elements.river.artifacts.model.Range;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/** Generate Fixings Table overview data structure to be stored in cache. */
+public class FixingsOverview implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    static Logger log = Logger.getLogger(FixingsOverview.class);
+
+    static final String DATE_FORMAT = "dd.MM.yyyy";
+
+    private static final String SQL_RIVER_ID = "SELECT" + "    id AS river_id," + "    km_up " + "FROM rivers " + "WHERE" + "    name = :name";
+
+    /** All kind-2 wsts from given river. */
+    private static final String SQL_FIXINGS = "SELECT" + "    id AS wst_id," + "    description " + "FROM wsts " + "WHERE"
+            + "    river_id = :river_id AND kind = 2";
+
+    /** Helper class to store data from batching fixing columns. */
+    static final class FixColumn {
+        int columnId;
+        Date startTime;
+        String name;
+
+        FixColumn(final int columnId, final Date startTime, final String name) {
+            this.columnId = columnId;
+            this.startTime = startTime;
+            this.name = name;
+        }
+    }
+
+    private String riverName;
+
+    private int riverId;
+
+    private boolean isKmUp;
+
+    private final List<Fixing> fixings;
+
+    private final Range extent;
+
+    public FixingsOverview() {
+        this.fixings = new ArrayList<>();
+        this.extent = new Range(Double.MAX_VALUE, -Double.MAX_VALUE);
+    }
+
+    public FixingsOverview(final String riverName) {
+        this();
+        this.riverName = riverName;
+    }
+
+    private boolean loadRiver(final Session session) {
+        final SQLQuery query = session.createSQLQuery(SQL_RIVER_ID).addScalar("river_id", StandardBasicTypes.INTEGER).addScalar("km_up",
+                StandardBasicTypes.BOOLEAN);
+
+        query.setString("name", this.riverName);
+
+        final List<Object[]> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("No river '" + this.riverName + "' found.");
+            return false;
+        }
+
+        final Object[] row = list.get(0);
+
+        this.riverId = (Integer) row[0];
+        this.isKmUp = (Boolean) row[1];
+
+        return true;
+    }
+
+    private void loadFixings(final Session session) {
+        final SQLQuery query = session.createSQLQuery(SQL_FIXINGS).addScalar("wst_id", StandardBasicTypes.INTEGER).addScalar("description",
+                StandardBasicTypes.STRING);
+
+        query.setInteger("river_id", this.riverId);
+
+        final List<Object[]> list = query.list();
+
+        if (list.isEmpty()) {
+            log.warn("River " + this.riverId + " has no fixings.");
+            // Its pretty fine to have no fixings.
+        }
+
+        for (final Object[] row : list) {
+            final int wstId = (Integer) row[0];
+            final String description = (String) row[1];
+            final Fixing fixing = new Fixing(wstId, description);
+            this.fixings.add(fixing);
+        }
+    }
+
+    private void loadFixingsColumns(final Session session) {
+
+        final FixColumnLoader loader = new FixColumnLoader(allFixingIds(), session);
+
+        for (final Fixing fixing : this.fixings) {
+            fixing.loadColumns(loader);
+        }
+    }
+
+    private List<Integer> allFixingIds() {
+        final List<Integer> ids = new ArrayList<>(this.fixings.size());
+        for (final Fixing fixing : this.fixings) {
+            ids.add(fixing.getId());
+        }
+        return ids;
+    }
+
+    private List<Integer> allColumnIds() {
+        final List<Integer> cIds = new ArrayList<>();
+        for (final Fixing fixing : this.fixings) {
+            fixing.allColumnIds(cIds);
+        }
+        return cIds;
+    }
+
+    private void loadFixingsColumnsKmRange(final Session session) {
+
+        final KMRangeLoader loader = new KMRangeLoader(allColumnIds(), session);
+
+        for (final Fixing fixing : this.fixings) {
+            fixing.loadColumnsKmRange(loader);
+        }
+    }
+
+    private void loadFixingsColumnsQRanges(final Session session, final GaugeFinder gaugeFinder) {
+
+        final ColumnQRangeLoader loader = new ColumnQRangeLoader(allColumnIds(), session);
+
+        for (final Fixing fixing : this.fixings) {
+            fixing.loadColumnsQRanges(loader, gaugeFinder);
+        }
+    }
+
+    private void adjustExtent() {
+        for (final Fixing fixing : this.fixings) {
+            fixing.adjustExtent(this.extent);
+        }
+    }
+
+    boolean load(final Session session) {
+
+        if (!loadRiver(session)) {
+            return false;
+        }
+
+        final GaugeFinderFactory gff = GaugeFinderFactory.getInstance();
+
+        final GaugeFinder gaugeFinder = gff.getGaugeFinder(this.riverId, this.isKmUp);
+
+        if (gaugeFinder == null) {
+            return false;
+        }
+
+        loadFixings(session);
+        loadFixingsColumns(session);
+        loadFixingsColumnsKmRange(session);
+
+        adjustExtent();
+
+        loadFixingsColumnsQRanges(session, gaugeFinder);
+
+        return true;
+    }
+
+    public static final Range FULL_EXTENT = new Range(-Double.MAX_VALUE, Double.MAX_VALUE);
+
+    public static final FixingColumnFilter ACCEPT = new FixingColumnFilter() {
+        @Override
+        public boolean accept(final FixingColumn column) {
+            return true;
+        }
+    };
+
+    public static class NotFilter implements FixingColumnFilter {
+        protected FixingColumnFilter child;
+
+        public NotFilter(final FixingColumnFilter child) {
+            this.child = child;
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            return !this.child.accept(column);
+        }
+    } // class NotFilter
+
+    public static abstract class ComponentFilter implements FixingColumnFilter {
+        protected List<FixingColumnFilter> children;
+
+        public ComponentFilter() {
+            this.children = new ArrayList<>();
+        }
+
+        public ComponentFilter(final List<FixingColumnFilter> children) {
+            this.children = children;
+        }
+
+        public ComponentFilter add(final FixingColumnFilter filter) {
+            this.children.add(filter);
+            return this;
+        }
+    } // class ComponentFilter
+
+    public static class OrFilter extends ComponentFilter {
+
+        public OrFilter() {
+        }
+
+        public OrFilter(final List<FixingColumnFilter> children) {
+            super(children);
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            for (final FixingColumnFilter child : this.children) {
+                if (child.accept(column)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    } // class OrFilter
+
+    public static class AndFilter extends ComponentFilter {
+
+        public AndFilter() {
+        }
+
+        public AndFilter(final List<FixingColumnFilter> children) {
+            super(children);
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            for (final FixingColumnFilter child : this.children) {
+                if (!child.accept(column)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    } // class AndFilter
+
+    public static class IdFilter implements FixingColumnFilter {
+
+        protected int columnId;
+
+        public IdFilter(final int columnId) {
+            this.columnId = columnId;
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            return column.getId() == this.columnId;
+        }
+    } // class IdFilter
+
+    /** Accept Fixing columns whose id is in id list. */
+    public static class IdsFilter implements FixingColumnFilter {
+
+        protected int[] columnIds;
+
+        public IdsFilter(final int[] columnIds) {
+            this.columnIds = columnIds;
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            final int cid = column.getId();
+            for (int i = this.columnIds.length - 1; i >= 0; --i) {
+                if (this.columnIds[i] == cid) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    } // class IdFilter
+
+    public static class DateFilter implements FixingColumnFilter {
+
+        protected Date date;
+
+        public DateFilter(final Date date) {
+            this.date = date;
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            return this.date.equals(column.getStartTime());
+        }
+    } // class DateFilter
+
+    public static class DateRangeFilter implements FixingColumnFilter {
+
+        protected Date start;
+        protected Date end;
+
+        public DateRangeFilter(final Date start, final Date end) {
+            if (start.before(end)) {
+                this.start = start;
+                this.end = end;
+            } else {
+                this.start = end;
+                this.end = start;
+            }
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            final Date date = column.getStartTime();
+            // start <= date <= end
+            return !(date.before(this.start) || date.after(this.end));
+        }
+    } // class DateRangeFilter
+
+    public static class SectorFilter implements FixingColumnFilter {
+
+        protected int sector;
+
+        public SectorFilter(final int sector) {
+            this.sector = sector;
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            for (final SectorRange s : column.getSectors()) {
+                if (s.getSector() == this.sector) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    } // class SectorFilter
+
+    public static class SectorRangeFilter implements FixingColumnFilter {
+
+        protected int min;
+        protected int max;
+
+        public SectorRangeFilter(final int min, final int max) {
+            this.min = Math.min(min, max);
+            this.max = Math.max(min, max);
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            for (final SectorRange s : column.getSectors()) {
+                final int v = s.getSector();
+                if (v < this.min || v > this.max) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    } // class SectorRangeFilter
+
+    public static class KmFilter implements FixingColumnFilter {
+
+        protected double km;
+
+        public KmFilter(final double km) {
+            this.km = km;
+        }
+
+        @Override
+        public boolean accept(final FixingColumn column) {
+            for (final SectorRange s : column.getSectors()) {
+                if (s.inside(this.km)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    } // class KmFilter
+
+    public void generateOverview(final Document document) {
+        generateOverview(document, FULL_EXTENT, ACCEPT);
+    }
+
+    /**
+     * @param range
+     *            can be null.
+     */
+    public List<FixingColumn> filter(final Range range, final FixingColumnFilter filter) {
+        final List<FixingColumn> allColumns = new ArrayList<>();
+
+        for (final Fixing fixing : this.fixings) {
+            fixing.addAllColumns(allColumns, range, filter);
+        }
+
+        Collections.sort(allColumns, Fixing.DATE_CMP);
+
+        return allColumns;
+    }
+
+    private static Range realRange(final List<FixingColumn> columns) {
+        Range range = null;
+        for (final FixingColumn column : columns) {
+            if (range == null) {
+                range = new Range(column);
+            } else {
+                range.extend(column);
+            }
+        }
+        return range;
+    }
+
+    private Element intersectingGauges(final Document document, final Range range) {
+        final Element gauges = document.createElement("gauges");
+
+        if (range == null) {
+            return gauges;
+        }
+
+        final GaugeFinderFactory gff = GaugeFinderFactory.getInstance();
+
+        final GaugeFinder gf = gff.getGaugeFinder(this.riverId, this.isKmUp);
+
+        if (gf == null) {
+            return gauges;
+        }
+
+        for (final GaugeRange gr : gf.getGauges()) {
+            if (gr.intersects(range)) {
+                final Element gauge = document.createElement("gauge");
+                gauge.setAttribute("from", String.valueOf(gr.getStart()));
+                gauge.setAttribute("to", String.valueOf(gr.getEnd()));
+                gauge.setAttribute("name", gr.getName());
+                gauges.appendChild(gauge);
+            }
+        }
+
+        return gauges;
+    }
+
+    /** Populate document with fixings, filtered by range and filter. */
+    public void generateOverview(final Document document, final Range range, final FixingColumnFilter filter) {
+        final List<FixingColumn> allColumns = filter(range, filter);
+
+        final Element fixingsElement = document.createElement("fixings");
+
+        final Element riverElement = document.createElement("river");
+
+        riverElement.setAttribute("from", String.valueOf(this.extent.getStart()));
+        riverElement.setAttribute("to", String.valueOf(this.extent.getEnd()));
+        riverElement.setAttribute("rid", String.valueOf(this.riverId));
+        riverElement.setAttribute("name", this.riverName);
+
+        fixingsElement.appendChild(riverElement);
+
+        fixingsElement.appendChild(intersectingGauges(document, realRange(allColumns)));
+
+        final SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT);
+
+        final Element esE = document.createElement("events");
+
+        for (final FixingColumn column : allColumns) {
+
+            final List<SectorRange> sectors = column.getSectors(range);
+
+            if (!sectors.isEmpty()) {
+                final Element eE = document.createElement("event");
+                eE.setAttribute("description", String.valueOf(column.getDescription()));
+                eE.setAttribute("cid", String.valueOf(column.getId()));
+                eE.setAttribute("date", df.format(column.getStartTime()));
+
+                for (final SectorRange sector : sectors) {
+                    final Element sE = document.createElement("sector");
+
+                    sE.setAttribute("from", String.valueOf(sector.getStart()));
+                    sE.setAttribute("to", String.valueOf(sector.getEnd()));
+                    sE.setAttribute("class", String.valueOf(sector.getSector()));
+
+                    eE.appendChild(sE);
+                }
+
+                esE.appendChild(eE);
+            }
+        }
+
+        fixingsElement.appendChild(esE);
+
+        document.appendChild(fixingsElement);
+    }
+
+    public Range getExtent() {
+        return this.extent;
+    }
+}
\ 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/model/fixings/FixingsOverviewFactory.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,81 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+
+public class FixingsOverviewFactory
+{
+    private static Logger log = Logger.getLogger(FixingsOverviewFactory.class);
+
+    public static final String CACHE_NAME = "fixings-overviews";
+
+    private FixingsOverviewFactory() {
+    }
+
+
+    public static FixingsOverview getOverview(String river) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "Looking for fixings overview for river '" + river + "'");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache not configured.");
+            }
+            return getUncached(river);
+        }
+
+        String key = "fix-over-" + river;
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Overview found in cache");
+            }
+            return (FixingsOverview)element.getValue();
+        }
+
+        FixingsOverview overview = getUncached(river);
+
+        if (overview != null) {
+            if (debug) {
+                log.debug("Store overview in cache.");
+            }
+            cache.put(new Element(key, overview));
+        }
+
+        return overview;
+    }
+
+    public static FixingsOverview getUncached(String river) {
+        FixingsOverview overview = new FixingsOverview(river);
+
+        Session session = SessionHolder.HOLDER.get();
+
+        return overview.load(session) ? overview : null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/KMRangeLoader.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,40 @@
+/** 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.model.fixings;
+
+import java.util.List;
+
+import org.dive4elements.river.utils.BatchLoader;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+final class KMRangeLoader extends BatchLoader<double[]> {
+
+    private static final String SQL_FIXING_COLUMN_KM_RANGE_BATCH = "SELECT " + "wst_column_id," + "MIN(position) AS start_km," + "MAX(position) AS stop_km "
+            + "FROM " + "wst_column_values " + "WHERE " + "wst_column_id IN ($IDS) " + "GROUP BY wst_column_id";
+
+    public KMRangeLoader(final List<Integer> columns, final Session session) {
+        super(columns, session, SQL_FIXING_COLUMN_KM_RANGE_BATCH);
+    }
+
+    @Override
+    protected void fill(final SQLQuery query) {
+        query.addScalar("wst_column_id", StandardBasicTypes.INTEGER).addScalar("start_km", StandardBasicTypes.DOUBLE).addScalar("stop_km",
+                StandardBasicTypes.DOUBLE);
+
+        final List<Object[]> ranges = query.list();
+        for (final Object[] r : ranges) {
+            final Integer cid = (Integer) r[0];
+            final double[] vs = new double[] { (Double) r[1], (Double) r[2] };
+            cache(cid, vs);
+        }
+    }
+}
\ 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/model/fixings/QRange.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,28 @@
+/** 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.model.fixings;
+
+import org.dive4elements.river.artifacts.model.Range;
+
+class QRange extends Range {
+
+    private static final long serialVersionUID = 1L;
+
+    double q;
+
+    public QRange() {
+    }
+
+    public QRange(final double start, final double end, final double q) {
+        super(start, end);
+
+        this.q = q;
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWD.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWD.java	Thu Aug 16 16:27:53 2018 +0200
@@ -27,9 +27,8 @@
         this.isOutlier = isOutlier;
     }
 
-    public QWD(final double q, final double w, final String description, final Date date, final boolean interpolated, final double deltaW, final int index,
-            final boolean isOutlier) {
-        super(q, w, description, date, interpolated, index);
+    public QWD(final double q, final double w, final Date date, final boolean interpolated, final double deltaW, final boolean isOutlier) {
+        super(q, w, date, interpolated);
 
         this.deltaW = deltaW;
         this.isOutlier = isOutlier;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWI.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/QWI.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,70 +8,49 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
-import org.dive4elements.river.artifacts.model.QW;
-
+import java.io.Serializable;
 import java.util.Date;
 
-public class QWI
-extends      QW
-{
-    protected String  description;
-    protected Date    date;
-    protected boolean interpolated;
-    protected int     index;
+public class QWI implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Date date;
+
+    private double q;
+
+    private double w;
+
+    private boolean interpolated;
 
     public QWI() {
     }
 
-    public QWI(double q, double w) {
-        super(q, w);
+    public QWI(final double q, final double w) {
+        this.q = q;
+        this.w = w;
     }
 
-    public QWI(
-        double  q,
-        double  w,
-        String  description,
-        Date    date,
-        boolean interpolated,
-        int     index
-    ) {
-        super(q, w);
-        this.description  = description;
-        this.date         = date;
+    public QWI(final double q, final double w, final Date date, final boolean interpolated) {
+        this.q = q;
+        this.w = w;
+        this.date = date;
         this.interpolated = interpolated;
-        this.index        = index;
     }
 
     public Date getDate() {
-        return date;
+        return this.date;
     }
 
-    public void setDate(Date date) {
-        this.date = date;
+    public double getQ() {
+        return this.q;
     }
 
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
+    public double getW() {
+        return this.w;
     }
 
     public boolean getInterpolated() {
-        return interpolated;
-    }
-
-    public void setInterpolated(boolean interpolated) {
-        this.interpolated = interpolated;
+        return this.interpolated;
     }
-
-    public int getIndex() {
-        return index;
-    }
-
-    public void setIndex(int index) {
-        this.index = index;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/RankRemapper.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.artifacts.model.fixings;
-
-import java.util.IdentityHashMap;
-
-import org.apache.log4j.Logger;
-
-import gnu.trove.TIntIntHashMap;
-
-public class RankRemapper {
-
-    private static Logger log = Logger.getLogger(RankRemapper.class);
-
-    private TIntIntHashMap                index2rank;
-    private IdentityHashMap<QWI, Boolean> visited;
-
-    public RankRemapper() {
-        index2rank = new TIntIntHashMap();
-        visited    = new IdentityHashMap<QWI, Boolean>();
-    }
-
-    public void toMap(int index) {
-        index2rank.put(index, index2rank.size());
-    }
-
-    public <I extends QWI> void remap(I qwi) {
-        if (!visited.containsKey(qwi)) {
-            int idx = qwi.index;
-            if (index2rank.containsKey(idx)) {
-                qwi.index = index2rank.get(idx);
-            } else if (log.isDebugEnabled()) {
-                log.debug("Cannot remap " + idx);
-            }
-            visited.put(qwi, true);
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/SectorRange.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,50 @@
+/** 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.model.fixings;
+
+import org.dive4elements.river.artifacts.model.Range;
+
+final class SectorRange extends Range {
+
+    private static final double EPSILON = 1e-2;
+
+    private static final long serialVersionUID = 1L;
+
+    private int sector;
+
+    public SectorRange() {
+    }
+
+    public SectorRange(final SectorRange other) {
+        super(other);
+
+        this.sector = other.sector;
+    }
+
+    public SectorRange(final Range range) {
+        super(range);
+    }
+
+    public int getSector() {
+        return this.sector;
+    }
+
+    public void setSector(final int sector) {
+        this.sector = sector;
+    }
+
+    public boolean enlarge(final SectorRange other) {
+        if (this.sector == other.sector && Math.abs(this.getEnd() - other.getStart()) < EPSILON) {
+            setEnd(other.getEnd());
+            return true;
+        }
+        return false;
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsKMChartService.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsKMChartService.java	Thu Aug 16 16:27:53 2018 +0200
@@ -26,15 +26,15 @@
 import org.dive4elements.artifacts.CallMeta;
 import org.dive4elements.artifacts.GlobalContext;
 import org.dive4elements.artifacts.Service;
-import org.dive4elements.river.artifacts.model.FixingsColumn;
-import org.dive4elements.river.artifacts.model.FixingsColumnFactory;
-import org.dive4elements.river.artifacts.model.FixingsFilterBuilder;
-import org.dive4elements.river.artifacts.model.FixingsOverview;
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
-import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
 import org.dive4elements.river.artifacts.model.GaugeFinder;
 import org.dive4elements.river.artifacts.model.GaugeFinderFactory;
 import org.dive4elements.river.artifacts.model.GaugeRange;
+import org.dive4elements.river.artifacts.model.fixings.FixingColumn;
+import org.dive4elements.river.artifacts.model.fixings.FixingColumnData;
+import org.dive4elements.river.artifacts.model.fixings.FixingsColumnFactory;
+import org.dive4elements.river.artifacts.model.fixings.FixingsFilterBuilder;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverviewFactory;
 import org.dive4elements.river.artifacts.model.fixings.QWI;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.backend.SessionHolder;
@@ -144,12 +144,12 @@
 
         final FixingsFilterBuilder ffb = new FixingsFilterBuilder(input);
 
-        final List<Fixing.Column> columns = overview.filter(ffb.getRange(), ffb.getFilter());
+        final List<FixingColumn> columns = overview.filter(ffb.getRange(), ffb.getFilter());
 
-        final List<Pair<Fixing.Column, FixingsColumn>> cols = new ArrayList<>();
+        final List<Pair<FixingColumn, FixingColumnData>> cols = new ArrayList<>();
 
-        for (final Fixing.Column col : columns) {
-            final FixingsColumn data = FixingsColumnFactory.INSTANCE.getColumnData(col);
+        for (final FixingColumn col : columns) {
+            final FixingColumnData data = FixingsColumnFactory.INSTANCE.getColumnData(col);
             if (data != null) {
                 cols.add(new Pair<>(col, data));
             }
@@ -176,7 +176,7 @@
         return new Output(out.toByteArray(), "image/" + format);
     }
 
-    protected static JFreeChart createChart(final List<Pair<Fixing.Column, FixingsColumn>> cols, final String river, final double km, final CallMeta callMeta) {
+    protected static JFreeChart createChart(final List<Pair<FixingColumn, FixingColumnData>> cols, final String river, final double km, final CallMeta callMeta) {
         final String labelFormat = Resources.getMsg(callMeta, I18N_CHART_LABEL_DATE, DEFAULT_CHART_LABEL_DATE);
 
         final QWSeriesCollection.LabelGenerator lg = new QWSeriesCollection.DateFormatLabelGenerator(labelFormat);
@@ -184,11 +184,11 @@
         final QWSeriesCollection dataset = new QWSeriesCollection(lg);
 
         final double[] w = new double[1];
-        for (final Pair<Fixing.Column, FixingsColumn> col : cols) {
+        for (final Pair<FixingColumn, FixingColumnData> col : cols) {
             final boolean interpolated = !col.getB().getW(km, w);
             final double q = col.getB().getQ(km);
             if (!Double.isNaN(w[0]) && !Double.isNaN(q)) {
-                final QWI qw = new QWI(q, w[0], col.getA().getDescription(), col.getA().getStartTime(), interpolated, 0);
+                final QWI qw = new QWI(q, w[0], col.getA().getStartTime(), interpolated);
                 dataset.add(qw);
             }
         }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsOverviewService.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/FixingsOverviewService.java	Thu Aug 16 16:27:53 2018 +0200
@@ -12,16 +12,11 @@
 import org.dive4elements.artifacts.GlobalContext;
 
 import org.dive4elements.artifacts.common.utils.XMLUtils;
-
-import org.dive4elements.river.artifacts.model.FixingsFilterBuilder;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
-
 import org.dive4elements.river.artifacts.model.Range;
-
-import org.dive4elements.river.artifacts.model.FixingsOverview;
-import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
-
+import org.dive4elements.river.artifacts.model.fixings.FixingColumnFilter;
+import org.dive4elements.river.artifacts.model.fixings.FixingsFilterBuilder;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverviewFactory;
 import org.apache.log4j.Logger;
 
 import org.w3c.dom.Document;
@@ -58,7 +53,7 @@
         if (overview != null) {
             FixingsFilterBuilder ffb = new FixingsFilterBuilder(data);
             Range  range  = ffb.getRange();
-            Filter filter = ffb.getFilter();
+            FixingColumnFilter filter = ffb.getFilter();
             overview.generateOverview(document, range, filter);
         }
         else {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/EventSelect.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/EventSelect.java	Thu Aug 16 16:27:53 2018 +0200
@@ -14,10 +14,10 @@
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
 import org.dive4elements.river.artifacts.D4EArtifact;
-import org.dive4elements.river.artifacts.model.FixingsOverview;
-import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
-import org.dive4elements.river.artifacts.model.FixingsOverview.IdFilter;
-import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
+import org.dive4elements.river.artifacts.model.fixings.FixingColumn;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverviewFactory;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.IdFilter;
 import org.dive4elements.river.artifacts.states.DefaultState;
 import org.dive4elements.river.utils.RiverUtils;
 import org.w3c.dom.Element;
@@ -73,7 +73,7 @@
         log.debug("Create label for value: " + value);
 
         final IdFilter filter = new IdFilter(value);
-        final List<Fixing.Column> columns = overview.filter(null, filter);
+        final List<FixingColumn> columns = overview.filter(null, filter);
         return columns.isEmpty() ? "" : columns.get(0).getDescription();
     }
 }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixAnalysisCompute.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixAnalysisCompute.java	Thu Aug 16 16:27:53 2018 +0200
@@ -13,6 +13,7 @@
 import java.util.Date;
 import java.util.List;
 
+import org.apache.commons.lang.math.DoubleRange;
 import org.apache.log4j.Logger;
 import org.dive4elements.artifactdatabase.state.Facet;
 import org.dive4elements.artifactdatabase.state.FacetActivity;
@@ -25,6 +26,7 @@
 import org.dive4elements.river.artifacts.model.DateRange;
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.model.ReportFacet;
+import org.dive4elements.river.artifacts.model.fixings.AnalysisPeriodEventResults;
 import org.dive4elements.river.artifacts.model.fixings.FixAnalysisCalculation;
 import org.dive4elements.river.artifacts.model.fixings.FixAnalysisEventsFacet;
 import org.dive4elements.river.artifacts.model.fixings.FixAnalysisPeriodsFacet;
@@ -37,17 +39,21 @@
 import org.dive4elements.river.artifacts.model.fixings.FixLongitudinalDeviationFacet;
 import org.dive4elements.river.artifacts.model.fixings.FixLongitudinalReferenceFacet;
 import org.dive4elements.river.artifacts.model.fixings.FixReferenceEventsFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixResultColumn;
+import org.dive4elements.river.artifacts.model.fixings.FixResultColumns;
 import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.states.DefaultState;
 import org.dive4elements.river.utils.Formatter;
 import org.dive4elements.river.utils.IdGenerator;
-import org.dive4elements.river.utils.UniqueDateFormatter;
 
 /**
  * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
  */
 public class FixAnalysisCompute extends DefaultState implements FacetTypes {
+
+    private static final long serialVersionUID = 1L;
+
     /** The log used in this class. */
     private static Logger log = Logger.getLogger(FixAnalysisCompute.class);
 
@@ -138,10 +144,14 @@
         final int qsE = access.getQSectorEnd();
 
         final DateFormat df = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy");
-        final DateFormat lf = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy'T'HH:mm");
+        final UniqueDateFormatter cf = new UniqueDateFormatter(df);
+
+        final AnalysisPeriodEventResults analysisEventResults = fr.getAnalysisEventResults();
 
         final DateRange[] periods = access.getAnalysisPeriods();
 
+        int facetIndex = 0;
+
         for (int i = 0; i < periods.length; i++) {
             final DateRange period = periods[i];
             final String startDate = df.format(period.getFrom());
@@ -172,22 +182,24 @@
                 final String dev = "Abweichung: " + description;
                 facets.add(new FixLongitudinalAvSectorFacet(facetNdx, FIX_SECTOR_AVERAGE_LS_DEVIATION + "_" + sectorNdx, dev));
                 facets.add(new FixAvSectorFacet(facetNdx, FIX_SECTOR_AVERAGE_WQ + "_" + sectorNdx, description));
-
             }
 
             final String eventDesc = Resources.getMsg(context.getMeta(), I18N_ANALYSIS, I18N_ANALYSIS);
 
-            final Collection<Date> aeds = fr.getAnalysisEventsDates(i);
-            final UniqueDateFormatter cf = new UniqueDateFormatter(df, lf, aeds);
+            final FixResultColumns analysisEventResult = analysisEventResults.getEventResults(i);
+            final Collection<FixResultColumn> columns = analysisEventResult.getSortedColumns();
 
-            int k = 0;
-            for (final Date d : aeds) {
-                int anaNdx = i << 8;
-                anaNdx = anaNdx | k;
-                facets.add(new FixAnalysisEventsFacet(anaNdx, FIX_ANALYSIS_EVENTS_DWT, eventDesc + (i + 1) + " - " + cf.format(d)));
-                facets.add(new FixLongitudinalAnalysisFacet(anaNdx, FIX_ANALYSIS_EVENTS_LS, eventDesc + (i + 1) + " - " + cf.format(d)));
-                facets.add(new FixAnalysisEventsFacet(anaNdx, FIX_ANALYSIS_EVENTS_WQ, eventDesc + (i + 1) + " - " + cf.format(d)));
-                k++;
+            for (final FixResultColumn analysisEventColumn : columns) {
+
+                final int columnId = analysisEventColumn.getColumnId();
+                final Date d = analysisEventColumn.getDate();
+                final DoubleRange stationRange = analysisEventColumn.getStationRange();
+
+                final String facetDescription = eventDesc + (i + 1) + " - " + cf.format(d);
+
+                facets.add(new FixAnalysisEventsFacet(facetIndex++, i, columnId, FIX_ANALYSIS_EVENTS_DWT, facetDescription, stationRange));
+                facets.add(new FixLongitudinalAnalysisFacet(facetIndex++, i, columnId, FIX_ANALYSIS_EVENTS_LS, facetDescription));
+                facets.add(new FixAnalysisEventsFacet(facetIndex++, i, columnId, FIX_ANALYSIS_EVENTS_WQ, facetDescription, stationRange));
             }
         }
 
@@ -196,21 +208,21 @@
         final String i18n_ref = Resources.getMsg(context.getMeta(), I18N_REFERENCEPERIOD_SHORT, I18N_REFERENCEPERIOD_SHORT);
         final String i18n_dev = Resources.getMsg(context.getMeta(), I18N_REFERENCEDEVIATION, I18N_REFERENCEDEVIATION);
 
-        final Collection<Date> reds = fr.getReferenceEventsDates();
-        final UniqueDateFormatter cf = new UniqueDateFormatter(df, lf, reds);
+        final FixResultColumns columns = fr.getFixResultColumns();
 
-        int i = 0;
-        for (final Date d : reds) {
-            int refNdx = idg.next() << 8;
-            refNdx |= i;
-            facets.add(new FixReferenceEventsFacet(refNdx, FIX_REFERENCE_EVENTS_DWT, i18n_ref + " - " + cf.format(d)));
-            refNdx = idg.next() << 8;
-            refNdx = refNdx | i;
-            facets.add(new FixLongitudinalReferenceFacet(refNdx, FIX_REFERENCE_EVENTS_LS, i18n_ref + " - " + cf.format(d)));
-            refNdx = idg.next() << 8;
-            refNdx |= i;
-            facets.add(new FixReferenceEventsFacet(refNdx, FIX_REFERENCE_EVENTS_WQ, i18n_ref + " - " + cf.format(d)));
-            i++;
+        final Collection<FixResultColumn> fixEvents = columns.getSortedColumns();
+        for (final FixResultColumn event : fixEvents) {
+
+            final int columnId = event.getColumnId();
+            final DoubleRange stationRange = event.getStationRange();
+
+            final Date date = event.getDate();
+            final String dateText = cf.format(date);
+            final String facetDescription = i18n_ref + " - " + dateText;
+
+            facets.add(new FixReferenceEventsFacet(facetIndex++, columnId, FIX_REFERENCE_EVENTS_DWT, facetDescription, stationRange));
+            facets.add(new FixLongitudinalReferenceFacet(facetIndex++, columnId, FIX_REFERENCE_EVENTS_LS, facetDescription));
+            facets.add(new FixReferenceEventsFacet(facetIndex++, columnId, FIX_REFERENCE_EVENTS_WQ, facetDescription, stationRange));
         }
 
         facets.add(new FixLongitudinalDeviationFacet(idg.next(), FIX_DEVIATION_LS, i18n_dev));
@@ -222,12 +234,10 @@
 
         final String i18n_refp = Resources.getMsg(context.getMeta(), I18N_REFERENCEPERIOD, I18N_REFERENCEPERIOD);
         facets.add(new DataFacet(idg.next(), FIX_REFERENCE_PERIOD_DWT, i18n_refp, ComputeType.ADVANCE, null, null));
-
         facets.add(new FixWQCurveFacet(idg.next(), "W/Q"));
+        facets.add(new FixDerivateFacet(idg.next(), FIX_DERIVATE_CURVE, Resources.getMsg(context.getMeta(), I18N_DERIVATIVE, I18N_DERIVATIVE)));
+        facets.add(new FixDeviationFacet(idg.next(), FIX_DEVIATION_DWT, Resources.getMsg(context.getMeta(), I18N_DEVIATION, I18N_DEVIATION)));
 
-        facets.add(new FixDerivateFacet(idg.next(), FIX_DERIVATE_CURVE, Resources.getMsg(context.getMeta(), I18N_DERIVATIVE, I18N_DERIVATIVE)));
-
-        facets.add(new FixDeviationFacet(idg.next(), FIX_DEVIATION_DWT, Resources.getMsg(context.getMeta(), I18N_DEVIATION, I18N_DEVIATION)));
         return res;
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixRealizingCompute.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixRealizingCompute.java	Thu Aug 16 16:27:53 2018 +0200
@@ -10,9 +10,9 @@
 
 import java.text.DateFormat;
 import java.util.Collection;
-import java.util.Date;
 import java.util.List;
 
+import org.apache.commons.lang.math.DoubleRange;
 import org.apache.log4j.Logger;
 import org.dive4elements.artifactdatabase.state.Facet;
 import org.dive4elements.artifacts.CallContext;
@@ -29,12 +29,13 @@
 import org.dive4elements.river.artifacts.model.fixings.FixRealizingCalculation;
 import org.dive4elements.river.artifacts.model.fixings.FixRealizingResult;
 import org.dive4elements.river.artifacts.model.fixings.FixReferenceEventsFacet;
+import org.dive4elements.river.artifacts.model.fixings.FixResultColumn;
+import org.dive4elements.river.artifacts.model.fixings.FixResultColumns;
 import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet;
 import org.dive4elements.river.artifacts.model.fixings.FixWaterlevelFacet;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.states.DefaultState;
 import org.dive4elements.river.utils.Formatter;
-import org.dive4elements.river.utils.UniqueDateFormatter;
 
 /**
  * State to compute the fixation realizing (vollmer) results.
@@ -43,6 +44,8 @@
  */
 public class FixRealizingCompute extends DefaultState implements FacetTypes {
 
+    private static final long serialVersionUID = 1L;
+
     /** The log used in this class. */
     private static Logger log = Logger.getLogger(FixRealizingCompute.class);
 
@@ -107,23 +110,25 @@
             }
         }
 
-        if (fixRes != null && wqkms.length > 0) {
-            final DateFormat df = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy");
-            final DateFormat lf = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy'T'HH:mm");
+        int facetIndex = 0;
 
-            final Collection<Date> reds = fixRes.getReferenceEventsDates();
-            final UniqueDateFormatter cf = new UniqueDateFormatter(df, lf, reds);
+        if (fixRes != null && wqkms.length > 0) {
 
-            int i = 0;
-            for (final Date d : reds) {
-                facets.add(new FixReferenceEventsFacet((1 << 9) | i, FIX_EVENTS, cf.format(d)));
-                i++;
+            final DateFormat df = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy");
+            final UniqueDateFormatter cf = new UniqueDateFormatter(df);
+
+            final FixResultColumns columns = fixRes.getFixResultColumns();
+            final Collection<FixResultColumn> fixEvents = columns.getSortedColumns();
+            for (final FixResultColumn event : fixEvents) {
+
+                final String facetDescription = cf.format(event.getDate());
+                final DoubleRange stationRange = event.getStationRange();
+
+                facets.add(new FixReferenceEventsFacet(facetIndex++, event.getColumnId(), FIX_EVENTS, facetDescription, stationRange));
             }
 
             facets.add(new DataFacet(CSV, "CSV data", ComputeType.ADVANCE, hash, id));
-
             facets.add(new DataFacet(WST, "WST data", ComputeType.ADVANCE, hash, id));
-
             facets.add(new DataFacet(PDF, "PDF data", ComputeType.ADVANCE, hash, id));
         }
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/LocationSelect.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/LocationSelect.java	Thu Aug 16 16:27:53 2018 +0200
@@ -13,9 +13,9 @@
 import org.dive4elements.artifacts.Artifact;
 
 import org.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverview;
+import org.dive4elements.river.artifacts.model.fixings.FixingsOverviewFactory;
 import org.dive4elements.river.artifacts.states.RangeState;
-import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
-import org.dive4elements.river.artifacts.model.FixingsOverview;
 
 
 /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/UniqueDateFormatter.java	Thu Aug 16 16:27:53 2018 +0200
@@ -0,0 +1,41 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.states.fixation;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+final class UniqueDateFormatter {
+
+    private final DateFormat df;
+
+    public UniqueDateFormatter(final DateFormat df) {
+        this.df = df;
+    }
+
+    public String format(final Date date) {
+
+        // FIXME: depends on zone of the dates inside the database, TODO: check this
+        final TimeZone zone = TimeZone.getTimeZone("GMT+1");
+        final Calendar instance = Calendar.getInstance(zone);
+        instance.setTime(date);
+
+        final int hour = instance.get(Calendar.HOUR_OF_DAY);
+
+        final String formatted = this.df.format(date);
+
+        if (hour == 0)
+            return formatted;
+
+        // return formatted;
+        return String.format("%s (%s)", formatted, hour);
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java	Thu Aug 16 16:27:53 2018 +0200
@@ -14,6 +14,8 @@
 import java.text.MessageFormat;
 import java.text.NumberFormat;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
 import java.util.List;
 import java.util.TreeMap;
 
@@ -28,8 +30,10 @@
 import org.dive4elements.river.artifacts.common.JasperReporter;
 import org.dive4elements.river.artifacts.common.MetaAndTableJRDataSource;
 import org.dive4elements.river.artifacts.model.CalculationResult;
-import org.dive4elements.river.artifacts.model.fixings.AnalysisPeriod;
+import org.dive4elements.river.artifacts.model.fixings.AnalysisPeriodEventResults;
 import org.dive4elements.river.artifacts.model.fixings.FixAnalysisResult;
+import org.dive4elements.river.artifacts.model.fixings.FixResultColumn;
+import org.dive4elements.river.artifacts.model.fixings.FixResultColumns;
 import org.dive4elements.river.artifacts.model.fixings.QWD;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
@@ -37,6 +41,7 @@
 import org.dive4elements.river.exports.AbstractExporter;
 import org.dive4elements.river.utils.Formatter;
 import org.dive4elements.river.utils.KMIndex;
+import org.dive4elements.river.utils.KMIndex.Entry;
 
 import au.com.bytecode.opencsv.CSVWriter;
 import net.sf.jasperreports.engine.JRException;
@@ -48,50 +53,45 @@
 
     private static final String JASPER_FILE = "/jasper/templates/fixanalysis.delta_wt.jrxml";
 
-    public static final String CSV_KM_HEADER = "export.fixings.deltawt.csv.header.km";
-
-    public static final String CSV_DELTA_W_HEADER = "export.fixings.deltawt.csv.header.deltaw";
-
-    public static final String CSV_Q_HEADER = "export.fixings.deltawt.csv.header.q";
-
-    public static final String CSV_W_HEADER = "export.fixings.deltawt.csv.header.w";
-
-    public static final String CSV_TRANGE_HEADER = "export.fixings.deltawt.csv.header.time.range";
-
-    public static final String CSV_T_HEADER = "export.fixings.deltawt.csv.header.t";
-
-    public static final String CSV_T_FORMAT = "export.fixings.deltawt.csv.t.format";
-
-    public static final String DEFAULT_CSV_KM_HEADER = "km";
-
-    public static final String DEFAULT_CSV_DELTA_W_HEADER = "\u0394 W [cm]";
-
-    public static final String DEFAULT_CSV_W_HEADER = "Wasserstand [m]";
-
-    public static final String DEFAULT_CSV_Q_HEADER = "Abfluss [m\u00b3/s]";
+    private static final String CSV_KM_HEADER = "export.fixings.deltawt.csv.header.km";
 
-    public static final String DEFAULT_CSV_T_HEADER = "Datum";
-
-    public static final String DEFAULT_CSV_TRANGE_DESC_HEADER = "Status";
-
-    public static final String CSV_REFERENCE = "export.fixings.deltawt.csv.reference";
-
-    public static final String CSV_ANALYSIS = "export.fixings.deltawt.csv.analysis";
-
-    public static final String DEFAULT_CSV_REFERENCE = "B";
+    private static final String CSV_DELTA_W_HEADER = "export.fixings.deltawt.csv.header.deltaw";
 
-    public static final String DEFAULT_CSV_ANALYSIS = "A{0,number,integer}";
-
-    public static final String DEFAULT_CSV_T_FORMAT = "dd.MM.yyyy";
-
-    protected List<KMIndex<AnalysisPeriod[]>> analysisPeriods;
+    private static final String CSV_Q_HEADER = "export.fixings.deltawt.csv.header.q";
 
-    protected List<KMIndex<QWD[]>> referenceEvents;
+    private static final String CSV_W_HEADER = "export.fixings.deltawt.csv.header.w";
 
-    public DeltaWtExporter() {
-        this.analysisPeriods = new ArrayList<>();
-        this.referenceEvents = new ArrayList<>();
-    }
+    private static final String CSV_TRANGE_HEADER = "export.fixings.deltawt.csv.header.time.range";
+
+    private static final String CSV_T_HEADER = "export.fixings.deltawt.csv.header.t";
+
+    private static final String CSV_T_FORMAT = "export.fixings.deltawt.csv.t.format";
+
+    private static final String DEFAULT_CSV_KM_HEADER = "km";
+
+    private static final String DEFAULT_CSV_DELTA_W_HEADER = "\u0394 W [cm]";
+
+    private static final String DEFAULT_CSV_W_HEADER = "Wasserstand [m]";
+
+    private static final String DEFAULT_CSV_Q_HEADER = "Abfluss [m\u00b3/s]";
+
+    private static final String DEFAULT_CSV_T_HEADER = "Datum";
+
+    private static final String DEFAULT_CSV_TRANGE_DESC_HEADER = "Status";
+
+    private static final String CSV_REFERENCE = "export.fixings.deltawt.csv.reference";
+
+    private static final String CSV_ANALYSIS = "export.fixings.deltawt.csv.analysis";
+
+    private static final String DEFAULT_CSV_REFERENCE = "B";
+
+    private static final String DEFAULT_CSV_ANALYSIS = "A{0,number,integer}";
+
+    private static final String DEFAULT_CSV_T_FORMAT = "dd.MM.yyyy";
+
+    private final List<AnalysisPeriodEventResults> analysisEvents = new ArrayList<>();
+
+    private final List<FixResultColumns> referenceEvents = new ArrayList<>();
 
     @Override
     protected void addData(final Object d) {
@@ -104,10 +104,12 @@
         final Object data = ((CalculationResult) d).getData();
         if (!(data instanceof FixAnalysisResult)) {
             log.warn("Invalid data stored in result.");
+            return;
         }
+
         final FixAnalysisResult result = (FixAnalysisResult) data;
-        this.analysisPeriods.add(result.getAnalysisPeriods());
-        this.referenceEvents.add(result.getFixings());
+        this.analysisEvents.add(result.getAnalysisEventResults());
+        this.referenceEvents.add(result.getFixResultColumns());
     }
 
     @Override
@@ -115,17 +117,16 @@
 
         writeCSVHeader(writer);
 
-        final TreeMap<Double, ArrayList<String[]>> sorted = getRows();
-        for (final ArrayList<String[]> list : sorted.values()) {
-            for (final String[] row : list) {
+        final TreeMap<Double, List<String[]>> sorted = getRows();
+        for (final List<String[]> list : sorted.values()) {
+            for (final String[] row : list)
                 writer.writeNext(row);
-            }
         }
 
         writer.flush();
     }
 
-    private TreeMap<Double, ArrayList<String[]>> getRows() {
+    private TreeMap<Double, List<String[]>> getRows() {
         final NumberFormat kmF = getKMFormatter();
         final NumberFormat dwF = getDeltaWFormatter();
         final NumberFormat qF = getQFormatter();
@@ -133,76 +134,64 @@
 
         final DateFormat dF = getDateFormatter();
 
-        final TreeMap<Double, ArrayList<String[]>> sorted = new TreeMap<>();
+        final TreeMap<Double, List<String[]>> sorted = new TreeMap<>();
 
         final String referenceS = getReference();
 
-        for (final KMIndex<QWD[]> reference : this.referenceEvents) {
-
-            for (final KMIndex.Entry<QWD[]> kmEntry : reference) {
+        for (final FixResultColumns referenceColumns : this.referenceEvents) {
 
-                final Double km = kmEntry.getKm();
+            appendRows(sorted, referenceColumns, referenceS, kmF, dwF, qF, wF, dF);
+        }
 
-                ArrayList<String[]> list = sorted.get(km);
+        final String analysisTemplate = getAnalysisTemplate();
 
+        int analysisCount = 1;
+        for (final AnalysisPeriodEventResults analysisPeriodEventResults : this.analysisEvents) {
+
+            final Collection<FixResultColumns> analysisResults = analysisPeriodEventResults.getEventResults();
+            for (final FixResultColumns analysisColumns : analysisResults) {
+
+                final String analyisS = MessageFormat.format(analysisTemplate, analysisCount);
+                appendRows(sorted, analysisColumns, analyisS, kmF, dwF, qF, wF, dF);
+            }
+
+            analysisCount++;
+        }
+
+        return sorted;
+    }
+
+    private void appendRows(final TreeMap<Double, List<String[]>> sorted, final FixResultColumns resultColumns, final String referenceS, final NumberFormat kmF,
+            final NumberFormat dwF, final NumberFormat qF, final NumberFormat wF, final DateFormat dF) {
+
+        final Collection<FixResultColumn> cols = resultColumns.getSortedColumns();
+        for (final FixResultColumn column : cols) {
+
+            final Date date = column.getDate();
+
+            final KMIndex<QWD> qwds = column.getQWDs();
+            for (final Entry<QWD> qwdEntry : qwds) {
+
+                final double km = qwdEntry.getKm();
+                final QWD qwd = qwdEntry.getValue();
+
+                List<String[]> list = sorted.get(km);
                 if (list == null) {
                     list = new ArrayList<>();
                     sorted.put(km, list);
                 }
 
-                final String kmS = kmF.format(kmEntry.getKm());
-                for (final QWD qwd : kmEntry.getValue()) {
-                    if( !qwd.isOutlier() ) {
-                        final String deltaWS = dwF.format(qwd.getDeltaW());
-                        final String qS = qF.format(qwd.getQ());
-                        final String wS = wF.format(qwd.getW());
-                        final String dateS = dF.format(qwd.getDate());
-    
-                        list.add(new String[] { kmS, dateS, qS, wS, referenceS, deltaWS });
-                    }
+                if (!qwd.isOutlier()) {
+                    final String kmS = kmF.format(km);
+                    final String deltaWS = dwF.format(qwd.getDeltaW());
+                    final String qS = qF.format(qwd.getQ());
+                    final String wS = wF.format(qwd.getW());
+                    final String dateS = dF.format(date);
+
+                    list.add(new String[] { kmS, dateS, qS, wS, referenceS, deltaWS });
                 }
             }
         }
-
-        final String analysisTemplate = getAnalysisTemplate();
-
-        for (final KMIndex<AnalysisPeriod[]> periods : this.analysisPeriods) {
-
-            for (final KMIndex.Entry<AnalysisPeriod[]> kmEntry : periods) {
-
-                final Double km = kmEntry.getKm();
-
-                ArrayList<String[]> list = sorted.get(km);
-
-                if (list == null) {
-                    list = new ArrayList<>();
-                    sorted.put(km, list);
-                }
-
-                final String kmS = kmF.format(kmEntry.getKm());
-                int analysisCount = 1;
-
-                for (final AnalysisPeriod period : kmEntry.getValue()) {
-                    // Typically resulting in A1,A2...
-                    final String analyisS = MessageFormat.format(analysisTemplate, analysisCount);
-                    final QWD[] qwds = period.getQWDs();
-
-                    if (qwds != null) {
-                        for (final QWD qwd : qwds) {
-                            final String deltaWS = dwF.format(qwd.getDeltaW());
-                            final String qS = qF.format(qwd.getQ());
-                            final String wS = wF.format(qwd.getW());
-                            final String dateS = dF.format(qwd.getDate());
-
-                            list.add(new String[] { kmS, dateS, qS, wS, analyisS, deltaWS });
-                        }
-                    }
-                    ++analysisCount;
-                }
-            }
-        }
-        return sorted;
-
     }
 
     /** Template to create "State" strings like A1,A2... */
@@ -275,16 +264,18 @@
 
     @Override
     protected void writePDF(final OutputStream out) {
+
         final MetaAndTableJRDataSource source = new MetaAndTableJRDataSource();
         final String jasperFile = Resources.getMsg(this.context.getMeta(), JASPER_FILE);
+
         addMetaData(source);
         try {
-            final TreeMap<Double, ArrayList<String[]>> sorted = getRows(); // Custom Result could be nice, too...
-            for (final ArrayList<String[]> list : sorted.values()) {
-                for (final String[] row : list) {
+            final TreeMap<Double, List<String[]>> sorted = getRows(); // Custom Result could be nice, too...
+            for (final List<String[]> list : sorted.values()) {
+                for (final String[] row : list)
                     source.addData(row);
-                }
             }
+
             final JasperReporter reporter = new JasperReporter();
             reporter.addReport(jasperFile, source);
             reporter.exportPDF(out);
--- a/artifacts/src/main/java/org/dive4elements/river/utils/KMIndex.java	Thu Aug 16 15:47:10 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/KMIndex.java	Thu Aug 16 16:27:53 2018 +0200
@@ -8,89 +8,87 @@
 
 package org.dive4elements.river.utils;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 import java.util.Iterator;
-
-import java.io.Serializable;
+import java.util.List;
 
 /** km to value, searchable. Tolerance is at 10cm. */
-public class KMIndex<A>
-implements   Serializable, Iterable<KMIndex.Entry<A>>
-{
-    public static final double EPSILON = 1e-4;
+public class KMIndex<A> implements Serializable, Iterable<KMIndex.Entry<A>> {
 
-    public static class Entry<A>
-    implements          Serializable, Comparable<Entry<A>>
-    {
-        protected double km;
-        protected A      value;
+    private static final long serialVersionUID = 1L;
 
-        public Entry(double km) {
+    private static final double EPSILON = 1e-4;
+
+    public static class Entry<A> implements Serializable, Comparable<Entry<A>> {
+        private static final long serialVersionUID = 1L;
+
+        private final double km;
+
+        private A value;
+
+        public Entry(final double km) {
             this.km = km;
         }
 
-        public Entry(double km, A value) {
-            this.km    = km;
+        public Entry(final double km, final A value) {
+            this.km = km;
             this.value = value;
         }
 
         public double getKm() {
-            return km;
+            return this.km;
         }
 
         public A getValue() {
-            return value;
-        }
-
-        public void setValue(A value) {
-            this.value = value;
+            return this.value;
         }
 
         @Override
-        public int compareTo(Entry<A> other) {
-            double diff = km - other.km;
-            if (diff < -EPSILON) return -1;
-            if (diff > +EPSILON) return +1;
+        public int compareTo(final Entry<A> other) {
+            final double diff = this.km - other.km;
+            if (diff < -EPSILON)
+                return -1;
+            if (diff > +EPSILON)
+                return +1;
             return 0;
         }
 
-        public boolean epsilonEquals(double km) {
-            return Math.abs(this.km - km) < EPSILON;
+        public boolean epsilonEquals(final double otherKm) {
+            return Math.abs(this.km - otherKm) < EPSILON;
         }
-    } // class Entry
+    }
 
-
-    protected List<Entry<A>> entries;
+    private List<Entry<A>> entries;
 
     public KMIndex() {
         this(10);
     }
 
-    public KMIndex(int capacity) {
-        entries = new ArrayList<>(capacity);
+    public KMIndex(final int capacity) {
+        this.entries = new ArrayList<>(capacity);
     }
 
-    public void add(double km, A value) {
-        entries.add(new Entry<>(km, value));
+    public void add(final double km, final A value) {
+        this.entries.add(new Entry<>(km, value));
     }
 
     public void sort() {
-        Collections.sort(entries);
+        Collections.sort(this.entries);
     }
 
     public int size() {
-        return entries.size();
+        return this.entries.size();
     }
 
-    public Entry<A> get(int idx) {
-        return entries.get(idx);
+    public Entry<A> get(final int idx) {
+        return this.entries.get(idx);
     }
 
     /** Return the first entry at km. */
-    public Entry<A> search(double km) {
-        for (Entry<A> entry: entries) {
+    public Entry<A> search(final double km) {
+        for (final Entry<A> entry : this.entries) {
             if (entry.epsilonEquals(km)) {
                 return entry;
             }
@@ -98,13 +96,13 @@
         return null;
     }
 
-    public Entry<A> binarySearch(double km) {
-        int index = Collections.binarySearch(entries, new Entry<A>(km));
-        return index >= 0 ? entries.get(index) : null;
+    public Entry<A> binarySearch(final double km) {
+        final int index = Collections.binarySearch(this.entries, new Entry<A>(km));
+        return index >= 0 ? this.entries.get(index) : null;
     }
 
+    @Override
     public Iterator<Entry<A>> iterator() {
-        return entries.iterator();
+        return this.entries.iterator();
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/utils/UniqueDateFormatter.java	Thu Aug 16 15:47:10 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.utils;
-
-import java.text.DateFormat;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-public class UniqueDateFormatter {
-
-    private static Logger log = Logger.getLogger(UniqueDateFormatter.class);
-
-    private DateFormat df;
-    private DateFormat lf;
-    private Map<String, int[]> collisions;
-
-    public UniqueDateFormatter(
-        DateFormat df,
-        DateFormat lf,
-        Collection<Date> dates
-    ) {
-        this.df = df;
-        this.lf = lf;
-        collisions = build(dates);
-    }
-
-    private Map<String, int []> build(Collection<Date> dates) {
-        Map<String, int []> collisions = new HashMap<String, int[]>();
-        for (Date d: dates) {
-            String s = df.format(d);
-            int [] count = collisions.get(s);
-            if (count == null) {
-                collisions.put(s, count = new int[1]);
-            }
-            if (++count[0] > 1) {
-                log.debug("date collsion found: " + d);
-            }
-        }
-        return collisions;
-    }
-
-    public String format(Date date) {
-        String s = df.format(date);
-        int [] count = collisions.get(s);
-        return count == null || count[0] < 2
-            ? s
-            : lf.format(date);
-    }
-}

http://dive4elements.wald.intevation.org