# HG changeset patch # User gernotbelger # Date 1541409487 -3600 # Node ID 4809e23ffd27c4208854de3590704f0fc4f831fb # Parent 1f6fbbe88af8267b08e651c0c42b6e5f072e90a3 FastAnnotations (Locations, POIs, Streckenfavoriten) deterministisch diff -r 1f6fbbe88af8 -r 4809e23ffd27 backend/src/main/java/org/dive4elements/river/model/FastAnnotations.java --- a/backend/src/main/java/org/dive4elements/river/model/FastAnnotations.java Tue Oct 30 17:46:52 2018 +0100 +++ b/backend/src/main/java/org/dive4elements/river/model/FastAnnotations.java Mon Nov 05 10:18:07 2018 +0100 @@ -8,81 +8,65 @@ package org.dive4elements.river.model; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; -import java.util.ArrayList; +import java.util.Iterator; import java.util.List; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; import java.util.NoSuchElementException; import java.util.regex.Pattern; -import java.io.Serializable; - +import org.dive4elements.river.backend.SessionHolder; +import org.dive4elements.river.backend.utils.StringUtil; +import org.hibernate.SQLQuery; import org.hibernate.Session; -import org.hibernate.SQLQuery; - import org.hibernate.type.StandardBasicTypes; -import org.dive4elements.river.backend.SessionHolder; +public class FastAnnotations implements Serializable { + private static final long serialVersionUID = 1L; -public class FastAnnotations -implements Serializable -{ - 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"; + 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"; + 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 KM_CMP = - new Comparator() { - @Override - public int compare(Annotation a, Annotation b) { - double diff = a.a - b.a; - if (diff < -EPSILON) return -1; - if (diff > +EPSILON) return +1; - return 0; - } - }; + public static final Comparator KM_CMP = new Comparator() { + @Override + public int compare(final Annotation a, final Annotation b) { + final double diff = a.a - b.a; - public static final class Annotation - implements Serializable - { + if (diff < -EPSILON) + return -1; + if (diff > +EPSILON) + return +1; + + return 0; + } + }; + + public static final class Annotation implements Serializable, Comparable { + private static final long serialVersionUID = 1L; private double a; private double b; private String position; @@ -94,62 +78,68 @@ public Annotation() { } - public Annotation(double a) { + public Annotation(final double a) { this.a = a; } - public Annotation( - double a, - double b, - String position, - String attribute, - String name, - double top, - double bottom - ) { - this.a = a; - this.b = b; - this.position = position; + 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; + this.name = name; + this.top = top; + this.bottom = bottom; } public double getA() { - return a; + return this.a; } public double getB() { - return b; + return this.b; } public String getPosition() { - return position; + return this.position; } public String getAttribute() { - return attribute; + return this.attribute; } public String getName() { - return name; + return this.name; } public double getTop() { - return top; + return this.top; } public double getBottom() { - return bottom; + return this.bottom; } @Override public String toString() { - return "[a=" + a + ";b=" + b + - ";pos=" + position + ";attr=" + attribute + - ";name=" + name + ";top=" + top + - ";bot=" + bottom + "]"; + 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 @@ -161,62 +151,62 @@ public static class NameFilter implements Filter { - private Pattern namePattern; + private final Pattern namePattern; - public NameFilter(String name) { + public NameFilter(final String name) { this.namePattern = Pattern.compile(name); } @Override - public boolean accept(Annotation annotation) { - return namePattern.matcher(annotation.getName()).matches(); + 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(Annotation annotation) { + public boolean accept(final Annotation annotation) { return true; } }; public static final Filter IS_POINT = new Filter() { @Override - public boolean accept(Annotation annotation) { + public boolean accept(final Annotation annotation) { return Double.isNaN(annotation.getB()); } }; public static final Filter IS_RANGE = new Filter() { @Override - public boolean accept(Annotation annotation) { + public boolean accept(final Annotation annotation) { return !Double.isNaN(annotation.getB()); } }; - private Annotation [] annotations; + private Annotation[] annotations; public FastAnnotations() { } - public FastAnnotations(Annotation [] annotations) { + public FastAnnotations(final Annotation[] annotations) { this.annotations = annotations; } - public FastAnnotations(String riverName) { + public FastAnnotations(final String riverName) { this(loadByRiverName(riverName)); } - public FastAnnotations(int riverId) { + public FastAnnotations(final int riverId) { this(loadByRiverId(riverId)); } - public FastAnnotations(Iterator iter) { + public FastAnnotations(final Iterator iter) { this(toArray(iter)); } public int size() { - return annotations.length; + return this.annotations.length; } public Iterator filter(final Filter filter) { @@ -227,23 +217,23 @@ @Override public boolean hasNext() { - return current != null; + return this.current != null; } @Override public Annotation next() { - if (current == null) { + if (this.current == null) { throw new NoSuchElementException(); } - Annotation result = current; - current = findNext(); + final Annotation result = this.current; + this.current = findNext(); return result; } private Annotation findNext() { - while (idx < annotations.length) { - Annotation annotation = annotations[idx++]; + while (this.idx < FastAnnotations.this.annotations.length) { + final Annotation annotation = FastAnnotations.this.annotations[this.idx++]; if (filter.accept(annotation)) { return annotation; } @@ -259,9 +249,9 @@ }; } - public static Annotation [] toArray(Iterator iter) { + public static Annotation[] toArray(final Iterator iter) { - ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); while (iter.hasNext()) { list.add(iter.next()); @@ -270,75 +260,84 @@ return list.toArray(new Annotation[list.size()]); } - public Annotation findByKm(double km) { - Annotation key = new Annotation(km); - int idx = Arrays.binarySearch(annotations, key, KM_CMP); - return idx < 0 ? null : annotations[idx]; + 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(String query) { - Session session = SessionHolder.HOLDER.get(); + 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); + 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(List list) { - Annotation [] anns = new Annotation[list.size()]; + private static Annotation[] buildAnnotations(final List list) { + final Annotation[] anns = new Annotation[list.size()]; // Names are likely the same because they are a type // like 'Pegel' or 'Hafen'. - HashMap names = new HashMap(); + // final HashMap names = new HashMap<>(); for (int i = 0; i < anns.length; ++i) { - Object [] data = list.get(i); - double a = ((Double)data[0]); - double b = data[1] != null ? (Double)data[1] : Double.NaN; - String position = (String)data[2]; - String attribute = (String)data[3]; - String name = (String)data[4]; - double top = data[5] != null ? (Double)data[5] : Double.NaN; - double bottom = data[6] != null ? (Double)data[6] : Double.NaN; + 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) { - String old = names.get(name); - if (old != null) { - name = old; - } - else { - names.put(name, name); - } - } + // 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); + 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(String riverName) { + public static Annotation[] loadByRiverName(final String riverName) { - SQLQuery query = createQuery(SQL_BY_RIVER_NAME); + final SQLQuery query = createQuery(SQL_BY_RIVER_NAME); query.setString("river_name", riverName); return buildAnnotations(query.list()); } - public static Annotation [] loadByRiverId(int riverId) { + public static Annotation[] loadByRiverId(final int riverId) { - SQLQuery query = createQuery(SQL_BY_RIVER_ID); + final SQLQuery query = createQuery(SQL_BY_RIVER_ID); query.setInteger("river_id", riverId); return buildAnnotations(query.list()); } } -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 1f6fbbe88af8 -r 4809e23ffd27 gwt-client/src/main/java/org/dive4elements/river/client/server/ChartServiceHelper.java --- a/gwt-client/src/main/java/org/dive4elements/river/client/server/ChartServiceHelper.java Tue Oct 30 17:46:52 2018 +0100 +++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/ChartServiceHelper.java Mon Nov 05 10:18:07 2018 +0100 @@ -230,7 +230,7 @@ ElementCreator ec) { log.debug("ChartServiceHelper.appendCurrentKm"); - + Element currentKm = ec.create("currentKm"); String km = req.get("km");