Mercurial > dive4elements > river
view backend/src/main/java/org/dive4elements/river/model/River.java @ 9016:6e5ff436febe
Added datacage select and chart display for TKH series loaded from database
author | mschaefer |
---|---|
date | Mon, 23 Apr 2018 15:18:48 +0200 |
parents | 50416a0df385 |
children | 7c8d62867876 |
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.math.BigDecimal; import java.math.MathContext; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import org.apache.log4j.Logger; import org.dive4elements.river.backend.SessionHolder; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.Type; @Entity @Table(name = "rivers") public class River implements Serializable { private static Logger log = Logger.getLogger(River.class); public static final MathContext PRECISION = new MathContext(6); public static final double EPSILON = 1e-5; // Tolerance for determining whether we are at the station of a gauge public static final double GAUGE_EPSILON = 0.1; public static final Comparator<Double> KM_CMP = new Comparator<Double>() { @Override public int compare(final Double a, final Double b) { final double diff = a - b; if (diff < -EPSILON) return -1; if (diff > EPSILON) return +1; return 0; } }; private Integer id; private Long officialNumber; private String name; private boolean kmUp; private String modelUuid; private List<Gauge> gauges; private Unit wstUnit; private SeddbName seddbName; @Id @SequenceGenerator( name = "SEQUENCE_RIVERS_ID_SEQ", sequenceName = "RIVERS_ID_SEQ", allocationSize = 1) @GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_RIVERS_ID_SEQ") @Column(name = "id") public Integer getId() { return this.id; } public void setId(final Integer id) { this.id = id; } @Column(name = "official_number") public Long getOfficialNumber() { return this.officialNumber; } public void setOfficialNumber(final Long officialNumber) { this.officialNumber = officialNumber; } @Column(name = "name") public String getName() { return this.name; } public void setName(final String name) { this.name = name; } @Type(type="numeric_boolean") @Column(name = "km_up") public boolean getKmUp() { return this.kmUp; } public void setKmUp(final boolean kmUp) { this.kmUp = kmUp; } @Column(name = "model_uuid") public String getModelUuid() { return this.modelUuid; } public void setModelUuid(final String modelUuid) { this.modelUuid = modelUuid; } public River() { } public River(final String name, final Unit wstUnit, final String modelUuid) { this.name = name; this.modelUuid = modelUuid; this.wstUnit = wstUnit; } @OneToMany @JoinColumn(name="river_id") public List<Gauge> getGauges() { return this.gauges; } public void setGauges(final List<Gauge> gauges) { this.gauges = gauges; } @OneToOne @JoinColumn(name = "wst_unit_id" ) public Unit getWstUnit() { return this.wstUnit; } public void setWstUnit(final Unit wstUnit) { this.wstUnit = wstUnit; } /** * Get alternative seddb name. * * This is the name should be used in seddb queries * and might differ from "our" backend db name. * * @return The name River in the seddb. */ public String nameForSeddb() { final SeddbName alt = getSeddbName(); if (alt == null) { return getName(); } return alt.getName(); } @OneToOne @JoinColumn(name = "seddb_name_id" ) public SeddbName getSeddbName() { return this.seddbName; } public void setSeddbName(final SeddbName name) { this.seddbName = name; } @Override public String toString() { return this.name != null ? this.name : ""; } /** * This method returns the gauges that intersect with <i>a</i> and * <i>b</i>, * * @param a A start point. * @param b An end point. * * @return the intersecting gauges. */ public List<Gauge> determineGauges(double a, double b) { final Session session = SessionHolder.HOLDER.get(); if (a > b) { final double t = a; a = b; b = t; } final Query query = session.createQuery( "from Gauge where river=:river " + "and not " + "((:b < least(range.a, range.b)) or" + " (:a > greatest(range.a, range.b)))" + "order by a"); query.setParameter("river", this); query.setParameter("a", new BigDecimal(a, PRECISION)); query.setParameter("b", new BigDecimal(b, PRECISION)); return query.list(); } public Gauge maxOverlap(double a, double b) { final List<Gauge> gauges = determineGauges(a, b); if (gauges == null) { return null; } if (a > b) { final double t = a; a = b; b = t; } double max = -Double.MAX_VALUE; Gauge result = null; for (final Gauge gauge: gauges) { final Range r = gauge.getRange(); double c = r.getA().doubleValue(); double d = r.getB().doubleValue(); if (c > d) { final double t = c; c = d; d = t; } final double start = c >= a ? c : a; final double stop = d <= b ? d : b; final double length = stop - start; if (length > max) { max = length; result = gauge; } } return result; } public Gauge determineGaugeByName(final String name) { final Session session = SessionHolder.HOLDER.get(); final Query query = session.createQuery( "from Gauge where river=:river and name=:name"); query.setParameter("river", this); query.setParameter("name", name); final List<Gauge> gauges = query.list(); return gauges.isEmpty() ? null : gauges.get(0); } public Gauge determineGaugeByPosition(final double p) { // Per default, we prefer the gauge downstream return determineGaugeByPosition(p, getKmUp()); } /** * @param p Station on this river for which the gauge is searched * @param kmLower At boundary of two gauge ranges, should gauge at lower * km be returned? */ public Gauge determineGaugeByPosition(final double p, final boolean kmLower) { final Session session = SessionHolder.HOLDER.get(); final Query query = session.createQuery( "from Gauge g where river=:river " + "and :p between " + "least(g.range.a, g.range.b) and " + "greatest(g.range.a, g.range.b)"); query.setParameter("river", this); query.setParameter("p", new BigDecimal(p, PRECISION)); final List<Gauge> gauges = query.list(); if (gauges.isEmpty()) { return null; } if (gauges.size() == 1) { return gauges.get(0); } if (gauges.size() > 2) { // TODO: database schema should prevent this. log.warn("More than two gauge ranges overlap km " + p + ". Returning arbitrary result."); } final Gauge g0 = gauges.get(0); final Gauge g1 = gauges.get(1); if (kmLower) { return g0.getStation().doubleValue() < g1.getStation().doubleValue() ? g0 : g1; } return g0.getStation().doubleValue() > g1.getStation().doubleValue() ? g0 : g1; } /** * @param s station at which the gauge is requested. * @return Gauge within tolerance at given station. null if there is none. */ public Gauge determineGaugeAtStation(final double s) { final Session session = SessionHolder.HOLDER.get(); final Query query = session.createQuery( "from Gauge where river.id=:river " + "and station between :a and :b"); query.setParameter("river", getId()); query.setParameter("a", new BigDecimal(s - GAUGE_EPSILON)); query.setParameter("b", new BigDecimal(s + GAUGE_EPSILON)); final List<Gauge> gauges = query.list(); if (gauges.size() > 1) { log.warn("More than one gauge found at km " + s + " within +-" + GAUGE_EPSILON + ". Returning arbitrary result."); } return gauges.isEmpty() ? null : gauges.get(0); } public double[] determineMinMaxQ() { final Session session = SessionHolder.HOLDER.get(); final Query query = session.createQuery( "select min(wqr.q) as min, max(wqr.q) as max " + "from Wst as w " + "join w.columns as wc " + "join wc.columnQRanges as wcqr " + "join wcqr.wstQRange as wqr " + "where w.kind = 0 and river_id = :river"); query.setParameter("river", getId()); final double minmax[] = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE }; final List<Object> results = query.list(); if (!results.isEmpty()) { final Object[] arr = (Object[]) results.get(0); final BigDecimal minq = (BigDecimal)arr[0]; final BigDecimal maxq = (BigDecimal)arr[1]; minmax[0] = minq.doubleValue(); minmax[1] = maxq.doubleValue(); } return minmax; } /** * Determine reference gauge dependent on direction of calculation * for a range calculation, otherwise dependent on flow direction. */ public Gauge determineRefGauge(final double[] range, final boolean isRange) { if (isRange) { return determineGaugeByPosition( range[0], range[0] > range[1]); } else { return determineGaugeByPosition(range[0]); } } /** * Returns the min and max distance of this river. The first position in the * resulting array contains the min distance, the second position the max * distance. * * @return the min and max distance of this river. */ public double[] determineMinMaxDistance() { final Session session = SessionHolder.HOLDER.get(); final Query query = session.createQuery( "select min(range.a), max(range.b) from Gauge " + "where river=:river " + "and range is not null"); query.setParameter("river", this); final List<Object[]> result = query.list(); if (!result.isEmpty()) { final Object[] minMax = result.get(0); if (minMax[0] != null && minMax[1] != null) { return new double[] { ((BigDecimal)minMax[0]).doubleValue(), ((BigDecimal)minMax[1]).doubleValue() }; } } return null; } public Map<Double, Double> queryGaugeDatumsKMs() { final List<Gauge> gauges = getGauges(); final Map<Double, Double> result = new TreeMap<>(KM_CMP); for (final Gauge gauge: gauges) { final BigDecimal km = gauge.getStation(); final BigDecimal datum = gauge.getDatum(); if (km != null && datum != null) { result.put(km.doubleValue(), datum.doubleValue()); } } return result; } /** * Searches the gauges list of the river for a gauge number or a gauge name */ public Gauge findGauge(final long number, final String name) { for (final Gauge gauge : getGauges()) { if (gauge.getOfficialNumber().longValue() == number) return gauge; if (gauge.getName().equalsIgnoreCase(name)) return gauge; } return null; } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :