view artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixingsOverview.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 artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java@3e93f29281bc
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