diff artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java @ 5838:5aa05a7a34b7

Rename modules to more fitting names.
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 15:23:37 +0200
parents flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java@bd047b71ab37
children 4897a58c8746
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java	Thu Apr 25 15:23:37 2013 +0200
@@ -0,0 +1,808 @@
+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;
+
+
+/** Generate Fixings Table chart. */
+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";
+
+    /** All columns from given wst. */
+    public static final String SQL_FIXING_COLUMNS =
+        "SELECT" +
+        "    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 = :wst_id " +
+        "ORDER BY position";
+
+    public static final String SQL_FIXING_COLUMN_Q_RANGES =
+        "SELECT" +
+        "    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 = :column_id " +
+        "ORDER BY r.a";
+
+    public static final String SQL_FIXING_COLUMN_KM_RANGE =
+        "SELECT" +
+        "    MIN(position) AS start_km," +
+        "    MAX(position) AS stop_km " +
+        "FROM" +
+        "    wst_column_values " +
+        "WHERE" +
+        "    wst_column_id = :column_id";
+
+
+    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(SQLQuery query) {
+                query.setInteger("column_id", columnId);
+
+                List<Object []> kms = query.list();
+
+                if (kms.isEmpty()) {
+                    log.warn("No km range for column " + columnId + ".");
+                }
+                else {
+                    Object [] obj = kms.get(0);
+                    start = (Double)obj[0];
+                    end   = (Double)obj[1];
+                }
+            }
+
+            public void loadQRanges(
+                SQLQuery    query,
+                GaugeFinder gaugeFinder
+            ) {
+                query.setInteger("column_id", columnId);
+                List<Object []> list = query.list();
+
+                List<QRange> qRanges = new ArrayList<QRange>(list.size());
+
+                for (Object [] row: list) {
+                    double q     = (Double)row[0];
+                    double start = (Double)row[1];
+                    double end   = (Double)row[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 loadColumns(SQLQuery query) {
+            query.setInteger("wst_id", wstId);
+            List<Object []> list = query.list();
+            for (Object [] row: list) {
+                int    columnId  = (Integer)row[0];
+                Date   startTime = (Date)   row[1];
+                String name      = (String) row[2];
+                columns.add(new Column(columnId, startTime, name));
+            }
+        }
+
+        public void loadColumnsKmRange(SQLQuery query) {
+            for (Column column: columns) {
+                column.loadKmRange(query);
+            }
+        }
+
+        public void adjustExtent(Range extent) {
+            for (Column column: columns) {
+                extent.extend(column);
+            }
+        }
+
+        public void loadColumnsQRanges(
+            SQLQuery    query,
+            GaugeFinder gaugeFinder
+        ) {
+            for (Column column: columns) {
+                column.loadQRanges(query, 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) {
+        SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMNS)
+            .addScalar("wst_column_id", StandardBasicTypes.INTEGER)
+            .addScalar("start_time",    StandardBasicTypes.DATE)
+            .addScalar("name",          StandardBasicTypes.STRING);
+
+        for (Fixing fixing: fixings) {
+            fixing.loadColumns(query);
+        }
+    }
+
+    protected void loadFixingsColumnsKmRange(Session session) {
+        SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMN_KM_RANGE)
+            .addScalar("start_km", StandardBasicTypes.DOUBLE)
+            .addScalar("stop_km",  StandardBasicTypes.DOUBLE);
+
+        for (Fixing fixing: fixings) {
+            fixing.loadColumnsKmRange(query);
+        }
+    }
+
+    protected void loadFixingsColumnsQRanges(
+        Session     session,
+        GaugeFinder gaugeFinder
+    ) {
+        SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMN_Q_RANGES)
+            .addScalar("q",        StandardBasicTypes.DOUBLE)
+            .addScalar("start_km", StandardBasicTypes.DOUBLE)
+            .addScalar("stop_km",  StandardBasicTypes.DOUBLE);
+
+        for (Fixing fixing: fixings) {
+            fixing.loadColumnsQRanges(query, 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) {
+            this.start = start;
+            this.end   = end;
+        }
+
+        @Override
+        public boolean accept(Fixing.Column column) {
+            Date date = column.getStartTime();
+            return start.compareTo(date) <= 0 && end.compareTo(date) >= 0;
+        }
+    } // 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);
+    }
+
+    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);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org