view artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsOverview.java @ 9646:0380717105ba

Implemented alternative fitting strategy for Log-Linear function.
author Gernot Belger <g.belger@bjoernsen.de>
date Mon, 02 Dec 2019 17:56:15 +0100
parents 9744ce3c3853
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.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;
    }
}

http://dive4elements.wald.intevation.org