Mercurial > dive4elements > river
view backend/src/main/java/org/dive4elements/river/model/FastAnnotations.java @ 9594:225e48df608c
Softwaretests...20181219 6.1: overflow day compute with 365.25 instead of 365.0
author | mschaefer |
---|---|
date | Tue, 05 Feb 2019 15:24:22 +0100 |
parents | 4809e23ffd27 |
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.model; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.regex.Pattern; import org.dive4elements.river.backend.SessionHolder; import org.dive4elements.river.backend.utils.StringUtil; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.hibernate.type.StandardBasicTypes; public class FastAnnotations implements Serializable { private static final long serialVersionUID = 1L; public static final String SQL_BY_RIVER_NAME = // "SELECT r.a AS a, r.b AS b, p.value AS position, " + // "at.value AS attribute, ant.name AS name, " + // "e.top AS top, e.bottom AS bottom " + // "FROM annotations an " + // "JOIN ranges r " + "ON an.range_id = r.id " + // "JOIN attributes at " + "ON an.attribute_id = at.id "// + "JOIN positions p " + "ON an.position_id = p.id " + // "JOIN rivers riv " + "ON r.river_id = riv.id " + // "LEFT JOIN annotation_types ant " + "ON an.type_id = ant.id " + // "LEFT JOIN edges e " + "ON an.edge_id = e.id " + // "WHERE riv.name = :river_name " + "ORDER BY r.a, position"; public static final String SQL_BY_RIVER_ID = "SELECT r.a AS a, r.b AS b, p.value AS position, "// + "at.value AS attribute, ant.name AS name, "// + "e.top AS top, e.bottom AS bottom " + "FROM annotations an " + // "JOIN ranges r " + "ON an.range_id = r.id " + // "JOIN attributes at " + "ON an.attribute_id = at.id "// + "JOIN positions p " + "ON an.position_id = p.id " + // "LEFT JOIN annotation_types ant " + "ON an.type_id = ant.id "// + "LEFT JOIN edges e " + "ON an.edge_id = e.id " + // "WHERE r.id = :river_id " + "ORDER BY r.a, position"; public static final double EPSILON = 1e-5; public static final Comparator<Annotation> KM_CMP = new Comparator<Annotation>() { @Override public int compare(final Annotation a, final Annotation b) { final double diff = a.a - b.a; if (diff < -EPSILON) return -1; if (diff > +EPSILON) return +1; return 0; } }; public static final class Annotation implements Serializable, Comparable<Annotation> { private static final long serialVersionUID = 1L; private double a; private double b; private String position; private String attribute; private String name; private double top; private double bottom; public Annotation() { } public Annotation(final double a) { this.a = a; } public Annotation(final double a, final double b, final String position, final String attribute, final String name, final double top, final double bottom) { this.a = a; this.b = b; this.position = position; this.attribute = attribute; this.name = name; this.top = top; this.bottom = bottom; } public double getA() { return this.a; } public double getB() { return this.b; } public String getPosition() { return this.position; } public String getAttribute() { return this.attribute; } public String getName() { return this.name; } public double getTop() { return this.top; } public double getBottom() { return this.bottom; } @Override public String toString() { return "[a=" + this.a + ";b=" + this.b + ";pos=" + this.position + ";attr=" + this.attribute + ";name=" + this.name + ";top=" + this.top + ";bot=" + this.bottom + "]"; } @Override public int compareTo(final Annotation o) { // Comparable interface introduced to make annotations deterministic (for testing etc) final int compareKmStart = Double.valueOf(this.a).compareTo(Double.valueOf(o.a)); if (compareKmStart != 0) return compareKmStart; // Although position must not be null by database definition, Null-Checks are provided for safety reasons if (StringUtil.isEmpty(this.position)) return +1; // leere Strings ans Ende return String.valueOf(this.position).compareTo(String.valueOf(o.position)); } } // class Annotation public interface Filter { boolean accept(Annotation annotation); } // interface Filter public static class NameFilter implements Filter { private final Pattern namePattern; public NameFilter(final String name) { this.namePattern = Pattern.compile(name); } @Override public boolean accept(final Annotation annotation) { return this.namePattern.matcher(annotation.getName()).matches(); } } // class NameFilter public static final Filter ALL = new Filter() { @Override public boolean accept(final Annotation annotation) { return true; } }; public static final Filter IS_POINT = new Filter() { @Override public boolean accept(final Annotation annotation) { return Double.isNaN(annotation.getB()); } }; public static final Filter IS_RANGE = new Filter() { @Override public boolean accept(final Annotation annotation) { return !Double.isNaN(annotation.getB()); } }; private Annotation[] annotations; public FastAnnotations() { } public FastAnnotations(final Annotation[] annotations) { this.annotations = annotations; } public FastAnnotations(final String riverName) { this(loadByRiverName(riverName)); } public FastAnnotations(final int riverId) { this(loadByRiverId(riverId)); } public FastAnnotations(final Iterator<Annotation> iter) { this(toArray(iter)); } public int size() { return this.annotations.length; } public Iterator<Annotation> filter(final Filter filter) { return new Iterator<Annotation>() { private int idx; private Annotation current = findNext(); @Override public boolean hasNext() { return this.current != null; } @Override public Annotation next() { if (this.current == null) { throw new NoSuchElementException(); } final Annotation result = this.current; this.current = findNext(); return result; } private Annotation findNext() { while (this.idx < FastAnnotations.this.annotations.length) { final Annotation annotation = FastAnnotations.this.annotations[this.idx++]; if (filter.accept(annotation)) { return annotation; } } return null; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } public static Annotation[] toArray(final Iterator<Annotation> iter) { final ArrayList<Annotation> list = new ArrayList<>(); while (iter.hasNext()) { list.add(iter.next()); } return list.toArray(new Annotation[list.size()]); } public Annotation findByKm(final double km) { final Annotation key = new Annotation(km); final int idx = Arrays.binarySearch(this.annotations, key, KM_CMP); if ((idx < 0)) return null; if (idx == 0) return this.annotations[idx]; // lowest possible index // REMARK: binary search my find any annotation at kmTest, but we want the first entry (because the list of annotations // is ordered by name) for (int lowestIndex = idx; lowestIndex > 0; lowestIndex--) { final double kmTest = this.annotations[lowestIndex].a; if (Math.abs(kmTest - this.annotations[idx].a) > EPSILON) return this.annotations[lowestIndex + 1]; } return this.annotations[0]; } private static SQLQuery createQuery(final String query) { final Session session = SessionHolder.HOLDER.get(); return session.createSQLQuery(query).addScalar("a", StandardBasicTypes.DOUBLE).addScalar("b", StandardBasicTypes.DOUBLE) .addScalar("position", StandardBasicTypes.STRING).addScalar("attribute", StandardBasicTypes.STRING).addScalar("name", StandardBasicTypes.STRING) .addScalar("top", StandardBasicTypes.DOUBLE).addScalar("bottom", StandardBasicTypes.DOUBLE); } private static Annotation[] buildAnnotations(final List<Object[]> list) { final Annotation[] anns = new Annotation[list.size()]; // Names are likely the same because they are a type // like 'Pegel' or 'Hafen'. // final HashMap<String, String> names = new HashMap<>(); for (int i = 0; i < anns.length; ++i) { final Object[] data = list.get(i); final double a = ((Double) data[0]); final double b = data[1] != null ? (Double) data[1] : Double.NaN; final String position = (String) data[2]; final String attribute = (String) data[3]; final String name = (String) data[4]; final double top = data[5] != null ? (Double) data[5] : Double.NaN; final double bottom = data[6] != null ? (Double) data[6] : Double.NaN; // if (name != null) { // final String old = names.get(name); // if (old != null) { // name = old; // } else { // names.put(name, name); // } // } anns[i] = new Annotation(a, b, position, attribute, name, top, bottom); } Arrays.sort(anns); // Comparable interface introduced to make annotations deterministic (for testing etc) return anns; } public static Annotation[] loadByRiverName(final String riverName) { final SQLQuery query = createQuery(SQL_BY_RIVER_NAME); query.setString("river_name", riverName); return buildAnnotations(query.list()); } public static Annotation[] loadByRiverId(final int riverId) { final SQLQuery query = createQuery(SQL_BY_RIVER_ID); query.setInteger("river_id", riverId); return buildAnnotations(query.list()); } }