view artifacts/src/main/java/org/dive4elements/river/artifacts/model/OfficialLineFinder.java @ 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 5e38e2924c07
children
line wrap: on
line source
/* 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.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.log4j.Logger;
import org.dive4elements.river.artifacts.D4EArtifact;
import org.dive4elements.river.artifacts.cache.CacheFactory;
import org.dive4elements.river.model.Gauge;
import org.dive4elements.river.model.MainValue;
import org.dive4elements.river.model.NamedMainValue;
import org.dive4elements.river.model.OfficialLine;
import org.dive4elements.river.model.River;
import org.dive4elements.river.model.TimeInterval;
import org.dive4elements.river.model.Wst;
import org.dive4elements.river.model.WstColumn;

public class OfficialLineFinder
{
    public static final String CACHE_NAME = "official-lines";

    private static Logger log = Logger.getLogger(OfficialLineFinder.class);

    // We will only have one entry in this cache.
    public static final String CACHE_KEY = CACHE_NAME;

    public static final double EPSILON = 1e-4;


    public static class ValueRange extends Range {

        private double value;
        private int    wstId;
        private int    columnPos;
        private String name;
        private String source;
        private Date   date;

        public ValueRange(
            double start,
            double end,
            double value,
            int    wstId,
            int    columnPos,
            String name,
            String source,
            Date   date
        ) {
            super(start, end);
            this.value     = value;
            this.wstId     = wstId;
            this.columnPos = columnPos;
            this.name      = name;
            this.source    = source;
            this.date      = date;
        }

        public boolean sameValue(double value) {
            return Math.abs(value - this.value) < EPSILON;
        }

        public int getWstId() {
            return wstId;
        }

        public int getColumnPos() {
            return columnPos;
        }

        public boolean intersectsValueRange(Range r) {
            return r.inside(value);
        }

        public String getName() {
            return name;
        }

        public String getSource() {
            return source;
        }

        public Date getDate() {
            return date;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof ValueRange)) {
                return false;
            }
            ValueRange r = (ValueRange)o;
            return wstId == r.wstId && columnPos == r.columnPos;
        }

        @Override
        public String toString() {
            return "[" + name +
                " value: " + value +
                " wstId: " + wstId +
                " pos: " + columnPos +
                " source: " + source +
                " date: " + date +
                " from: " + getStart() +
                " to: " + getEnd() + "]";
        }
    }

    public OfficialLineFinder() {
    }

    public static Map<String, List<ValueRange>> getAll() {
        Cache cache = CacheFactory.getCache(CACHE_NAME);

        if (cache == null) {
            return getAllUncached();
        }

        Element element  = cache.get(CACHE_KEY);

        if (element != null) {
            return (Map<String, List<ValueRange>>)element.getValue();
        }

        Map<String, List<ValueRange>> result = getAllUncached();
        if (result != null) {
            cache.put(new Element(CACHE_KEY, result));
        }
        return result;

    }

    public static Map<String, List<ValueRange>> getAllUncached() {

        boolean debug = log.isDebugEnabled();

        Map<String, List<ValueRange>> rivers2officialLines =
            new HashMap<String, List<ValueRange>>();

        for (OfficialLine line: OfficialLine.fetchAllOfficalLines()) {
            NamedMainValue nmv    = line.getNamedMainValue();
            Integer        mnvId  = nmv.getId();
            WstColumn      wc     = line.getWstColumn();
            Wst            wst    = wc.getWst();
            TimeInterval   ti     = wc.getTimeInterval();
            Date           date   = ti != null ? ti.getStartTime() : null;
            String         source = wc.getSource();

            List<ValueRange> ranges = new ArrayList<ValueRange>();

            River river = wst.getRiver();
            List<Gauge> gauges = river.getGauges();
            for (Gauge gauge: gauges) {
                List<MainValue> mainValues = gauge.getMainValues();
                for (MainValue mainValue: mainValues) {
                    NamedMainValue tnmv = mainValue.getMainValue();
                    if (tnmv.getId().equals(mnvId)) {
                        // found gauge with this main value
                        double from  = gauge.getRange().getA().doubleValue();
                        double to    = gauge.getRange().getB().doubleValue();
                        double value = mainValue.getValue().doubleValue();
                        int    wstId = wst.getId();
                        int    pos   = wc.getPosition();
                        ValueRange range = new ValueRange(
                            from, to, value, wstId, pos,
                            nmv.getName(), source, date);

                        if (debug) {
                            log.debug(
                                "river: " + river.getName() +
                                " gauge: " + gauge.getName() +
                                " ol: " + range);
                        }
                        ranges.add(range);
                        break;
                    }
                }
            }

            if (!ranges.isEmpty()) {
                String rname = river.getName();
                List<ValueRange> old = rivers2officialLines.get(rname);
                if (old != null) {
                    old.addAll(ranges);
                }
                else {
                    rivers2officialLines.put(rname, ranges);
                }
            }
        }

        return rivers2officialLines;
    }

    public static final Range MAX_RANGE =
        new Range(-Double.MAX_VALUE, +Double.MAX_VALUE);

    private static final String nn(String s) {
        return s != null ? s : "";
    }

    public static Range extractRange(D4EArtifact artifact) {

        String mode      = nn(artifact.getDataAsString("ld_mode"));
        String locations = nn(artifact.getDataAsString("ld_locations"));
        String from      = nn(artifact.getDataAsString("ld_from"));
        String to        = nn(artifact.getDataAsString("ld_to"));

        if (log.isDebugEnabled()) {
            log.debug("ld_mode: '" + mode + "'");
            log.debug("ld_locations: '" + locations + "'");
            log.debug("ld_from: '" + from + "'");
            log.debug("ld_to: '" + to + "'");
        }

        if (mode.equals("location")) {
            try {
                String loc = locations.replace(" ", "");
                String[] split = loc.split(",");
                if (split.length < 1) {
                    return MAX_RANGE;
                }
                double min = Double.parseDouble(split[0]);
                double max = min;
                for (int i = 1; i < split.length; ++i) {
                    double v = Double.parseDouble(split[i]);
                    if (v > max) max = v;
                    if (v < min) min = v;
                }
                return new Range(min, max);
            }
            catch (NumberFormatException nfe) {
                return MAX_RANGE;
            }
        }
        try {
            double f = Double.parseDouble(from);
            double t = Double.parseDouble(to);
            if (f > t) {
                return new Range(t, f);
            }
            return new Range(f, t);
        }
        catch (NumberFormatException nfe) {
            return MAX_RANGE;
        }
    }

    private static List<ValueRange> filterByRange(
        Range range,
        List<ValueRange> ranges
    ) {
        List<ValueRange> list = new ArrayList<ValueRange>(ranges.size());
        for (ValueRange r: ranges) {
            if (r.intersects(range)) {
                list.add(r);
            }
        }
        return list;
    }

    private static List<ValueRange> filterByQRange(
        Range range,
        List<ValueRange> ranges
    ) {
        List<ValueRange> list = new ArrayList<ValueRange>(ranges.size());
        for (ValueRange r: ranges) {
            if (r.intersectsValueRange(range) && !list.contains(r)) {
                list.add(r);
            }
        }
        return list;
    }

    private static List<ValueRange> filterByQValues(
        double[] values,
        List<ValueRange> ranges
    ) {
        List<ValueRange> list = new ArrayList<ValueRange>(ranges.size());
        for (ValueRange r: ranges) {
            for (double val: values) {
                if (r.sameValue(val) && !list.contains(r)) {
                    list.add(r);
                }
            }
        }
        return list;
    }

    private static boolean isQ(D4EArtifact artifact) {
        Boolean b = artifact.getDataAsBoolean("wq_isq");
        return b != null && b;
    }

    private static boolean isRange(D4EArtifact artifact) {
        Boolean b = artifact.getDataAsBoolean("wq_isrange");
        return b != null && b;
    }

    public static final Range Q_OUT_OF_RANGE = new Range(-10000, -9999);

    private static double[] singleQs(D4EArtifact artifact) {
        String singleData = nn(artifact.getDataAsString("wq_single"));
        String[] values = singleData.split(" ");
        double[] ret = new double[values.length];
        int i = 0;

        for (String value: values) {
            try {
                ret[i] = Double.parseDouble(value);
            }
            catch (NumberFormatException nfe) {
                ret[i] = -1; // INVALID_Q_VALUE
            }
            i++;
        }

        return ret;
    }

    private static Range qRange(D4EArtifact artifact) {
        try {
            Double from = artifact.getDataAsDouble("wq_from");
            Double to   = artifact.getDataAsDouble("wq_to");

            if (from == null || to == null) {
                return Q_OUT_OF_RANGE;
            }
            double f = from;
            double t = to;
            return new Range(Math.min(f, t), Math.max(f, t));
        }
        catch (NumberFormatException nfe) {
            return Q_OUT_OF_RANGE;
        }
    }

    private static Range tripleQRange(D4EArtifact artifact) {
        String rangesData = nn(artifact.getDataAsString("wq_values"));

        double min =  Double.MAX_VALUE;
        double max = -Double.MAX_VALUE;

        for (String range: rangesData.split(":")) {
            String [] parts = range.split(";");
            if (parts.length < 3) {
                continue;
            }
            String [] values = parts[2].split(",");
            for (String value: values) {
                try {
                    double x = Double.parseDouble(value);
                    if (x < min) min = x;
                    if (x > max) max = x;
                }
                catch (NumberFormatException nfe) {
                }
            }
        }

        return min == Double.MAX_VALUE
            ? Q_OUT_OF_RANGE
            : new Range(min, max);
    }

    public static List<ValueRange> findOfficialLines(D4EArtifact artifact) {

        if (!isQ(artifact)) { // Only handle Q calculations
            return Collections.<ValueRange>emptyList();
        }

        Map<String, List<ValueRange>> rivers2officialLines = getAll();

        String riverName = nn(artifact.getDataAsString("river"));

        List<ValueRange> ranges = rivers2officialLines.get(riverName);

        if (ranges == null) {
            return Collections.<ValueRange>emptyList();
        }
        boolean debug = log.isDebugEnabled();

        if (debug) {
            log.debug("Before range filter:" + ranges);
        }

        ranges = filterByRange(extractRange(artifact), ranges);

        if (debug) {
            log.debug("After range filter:" + ranges);
        }

        if (ranges.isEmpty()) {
            return Collections.<ValueRange>emptyList();
        }

        if (isRange(artifact)) {
            Range qRange = qRange(artifact);
            if (qRange == Q_OUT_OF_RANGE) {
                qRange = tripleQRange(artifact);
            }
            ranges = filterByQRange(qRange, ranges);
            if (debug) {
                log.debug("Q range filter: " + qRange);
            }
        } else {
            ranges = filterByQValues(singleQs(artifact), ranges);
        }

        if (debug) {
            log.debug("After q range filter: " + ranges);
        }

        return ranges;
    }
}

http://dive4elements.wald.intevation.org