teichmann@5844: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5844: * Software engineering by Intevation GmbH teichmann@5844: * teichmann@5992: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5844: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5992: * documentation coming with Dive4Elements River for details. teichmann@5844: */ teichmann@5844: teichmann@5829: package org.dive4elements.river.model; sascha@1194: sascha@1194: import java.io.Serializable; sascha@1194: sascha@1194: import java.util.List; sascha@2380: import java.util.ArrayList; sascha@2380: sascha@2380: import java.awt.geom.Point2D; sascha@1194: sascha@1194: import javax.persistence.Entity; sascha@1194: import javax.persistence.Id; sascha@1194: import javax.persistence.Table; sascha@1194: import javax.persistence.GeneratedValue; sascha@1194: import javax.persistence.Column; sascha@1194: import javax.persistence.SequenceGenerator; sascha@1194: import javax.persistence.GenerationType; sascha@1194: import javax.persistence.OneToOne; sascha@1194: import javax.persistence.OneToMany; sascha@1210: import javax.persistence.OrderBy; sascha@1194: import javax.persistence.JoinColumn; sascha@1194: sascha@2379: import java.math.MathContext; sascha@2379: import java.math.BigDecimal; sascha@2379: sascha@2379: import org.hibernate.Session; sascha@2380: import org.hibernate.SQLQuery; sascha@2379: import org.hibernate.Query; sascha@2379: sascha@2380: import org.hibernate.type.StandardBasicTypes; sascha@2380: teichmann@5829: import org.dive4elements.river.backend.SessionHolder; sascha@2379: felix@6538: import org.apache.log4j.Logger; felix@6538: sascha@1194: @Entity sascha@1194: @Table(name = "cross_sections") sascha@1194: public class CrossSection sascha@1194: implements Serializable sascha@1194: { felix@6538: private static Logger logger = felix@6538: Logger.getLogger(CrossSection.class); felix@6538: sascha@2379: public static final MathContext PRECISION = new MathContext(6); sascha@2379: sascha@2380: public static final String SQL_FAST_CROSS_SECTION_LINES = sascha@2380: "SELECT km, x, y, csl.id AS csl_id " + sascha@2380: "FROM cross_section_lines csl JOIN cross_section_points csp " + sascha@2380: "ON csp.cross_section_line_id = csl.id " + sascha@2380: "WHERE csl.cross_section_id = :cs_id AND " + sascha@3334: "km between :from_km AND :to_km " + felix@6270: "ORDER BY csl.km, csl.id, csp.col_pos"; sascha@2380: felix@6536: public static final String SQL_MIN_MAX = felix@6536: "SELECT * FROM ( "+ felix@6536: "SELECT cross_section_id, MIN(km) AS minkm, MAX(km) AS maxkm " + felix@6536: "FROM cross_section_lines " + felix@6536: "WHERE cross_section_id IN " + felix@6536: " (SELECT id FROM cross_sections WHERE river_id = :river_id) " + felix@6536: " GROUP BY cross_section_id" + felix@6621: ") cs_ranges " + felix@6622: "JOIN cross_sections cs ON cs_ranges.cross_section_id = cs.id " + felix@6622: "LEFT OUTER JOIN time_intervals ON cs.time_interval_id = time_intervals.id " + felix@6538: "WHERE :km BETWEEN minkm AND maxkm " + felix@7017: "ORDER BY stop_time desc, start_time desc, :km - minkm"; felix@6536: // Order by time interval missing. felix@6536: sascha@1203: private Integer id; sascha@1203: private River river; sascha@1203: private TimeInterval timeInterval; sascha@1203: private String description; sascha@1203: private List lines; sascha@1194: sascha@1194: public CrossSection() { sascha@1194: } sascha@1194: sascha@1204: public CrossSection( sascha@1204: River river, sascha@1204: TimeInterval timeInterval, sascha@1204: String description sascha@1204: ) { sascha@1204: this.river = river; sascha@1204: this.timeInterval = timeInterval; sascha@1204: this.description = description; sascha@1204: } sascha@1204: sascha@1194: @Id sascha@1194: @SequenceGenerator( sascha@1194: name = "SEQUENCE_CROSS_SECTIONS_ID_SEQ", sascha@1194: sequenceName = "CROSS_SECTIONS_ID_SEQ", sascha@1194: allocationSize = 1) sascha@1194: @GeneratedValue( sascha@1194: strategy = GenerationType.SEQUENCE, sascha@1194: generator = "SEQUENCE_CROSS_SECTIONS_ID_SEQ") sascha@1194: @Column(name = "id") sascha@1194: public Integer getId() { sascha@1194: return id; sascha@1194: } sascha@1194: sascha@1194: public void setId(Integer id) { sascha@1194: this.id = id; sascha@1194: } sascha@1194: sascha@1194: @OneToOne sascha@1194: @JoinColumn(name = "river_id") sascha@1194: public River getRiver() { sascha@1194: return river; sascha@1194: } sascha@1194: sascha@1194: public void setRiver(River river) { sascha@1194: this.river = river; sascha@1194: } sascha@1194: sascha@1194: @OneToOne sascha@1194: @JoinColumn(name = "time_interval_id") sascha@1194: public TimeInterval getTimeInterval() { sascha@1194: return timeInterval; sascha@1194: } sascha@1194: sascha@1194: public void setTimeInterval(TimeInterval timeInterval) { sascha@1194: this.timeInterval = timeInterval; sascha@1194: } sascha@1194: sascha@1202: @Column(name = "description") sascha@1202: public String getDescription() { sascha@1202: return description; sascha@1202: } sascha@1202: sascha@1202: public void setDescription(String description) { sascha@1202: this.description = description; sascha@1202: } sascha@1202: sascha@1194: @OneToMany sascha@1210: @OrderBy("km") sascha@1194: @JoinColumn(name="cross_section_id") sascha@1203: public List getLines() { sascha@1203: return lines; sascha@1194: } sascha@1194: sascha@1203: public void setLines(List lines) { sascha@1203: this.lines = lines; sascha@1194: } sascha@2379: sascha@2379: public List getLines(double startKm, double endKm) { sascha@2379: Session session = SessionHolder.HOLDER.get(); sascha@2379: Query query = session.createQuery( sascha@2379: "from CrossSectionLine where crossSection=:crossSection " + sascha@2379: "and km between :startKm and :endKm order by km"); sascha@2379: query.setParameter("crossSection", this); sascha@2379: query.setParameter("startKm", new BigDecimal(startKm, PRECISION)); sascha@2379: query.setParameter("endKm", new BigDecimal(endKm, PRECISION)); sascha@2379: sascha@2379: return query.list(); sascha@2379: } sascha@2380: felix@6269: /** Get Lines from startkm to endkm, fast because direct usage of sql. */ sascha@2380: public List getFastLines( sascha@2380: double startKm, sascha@2380: double endKm sascha@2380: ) { sascha@2380: Session session = SessionHolder.HOLDER.get(); sascha@2380: sascha@2380: SQLQuery sqlQuery = session.createSQLQuery(SQL_FAST_CROSS_SECTION_LINES) sascha@2380: .addScalar("km", StandardBasicTypes.DOUBLE) sascha@2380: .addScalar("x", StandardBasicTypes.DOUBLE) sascha@2380: .addScalar("y", StandardBasicTypes.DOUBLE) sascha@2380: .addScalar("csl_id", StandardBasicTypes.INTEGER); sascha@2380: sascha@2380: sqlQuery sascha@2380: .setInteger("cs_id", getId()) sascha@2380: .setDouble("from_km", startKm) sascha@2380: .setDouble("to_km", endKm); sascha@2380: sascha@2380: List results = sqlQuery.list(); sascha@2380: sascha@2380: ArrayList points = new ArrayList(500); sascha@2380: ArrayList lines = sascha@2380: new ArrayList(); sascha@2380: sascha@2380: Integer lastId = null; sascha@2380: Double lastKm = null; sascha@2380: sascha@2380: for (Object [] result: results) { sascha@2380: Double km = (Double)result[0]; sascha@2380: Double x = (Double)result[1]; sascha@2380: Double y = (Double)result[2]; sascha@2380: Integer id = (Integer)result[3]; sascha@2380: sascha@2380: if (lastId != null && !lastId.equals(id)) { sascha@2380: points.trimToSize(); sascha@2380: FastCrossSectionLine line = sascha@2380: new FastCrossSectionLine(lastKm, points); sascha@2380: lines.add(line); sascha@2380: points = new ArrayList(500); sascha@2380: } sascha@2380: sascha@2380: Point2D p = new Point2D.Double(x, y); sascha@2380: sascha@2380: if (CrossSectionLine.isValid(p)) { sascha@2380: points.add(p); sascha@2380: } sascha@2380: sascha@2380: lastKm = km; sascha@2380: lastId = id; sascha@2380: } sascha@2380: sascha@2380: if (lastId != null) { sascha@2380: points.trimToSize(); sascha@2380: FastCrossSectionLine line = sascha@2380: new FastCrossSectionLine(lastKm, points); sascha@2380: lines.add(line); sascha@2380: } sascha@2380: sascha@2380: lines.trimToSize(); sascha@2380: sascha@2380: return lines; sascha@2380: } felix@6538: felix@6538: /** felix@6538: * True if the given section is the "newest" for that river and has values at km. felix@6538: * @param km Given station. felix@6538: * @return true if the section has the most advanced end of its validity interval felix@6538: * or the most advanced start of its validity interval. felix@6538: */ felix@6538: public boolean shouldBeMaster(double km) { felix@6538: Session session = SessionHolder.HOLDER.get(); felix@6538: felix@6538: SQLQuery sqlQuery = session.createSQLQuery(SQL_MIN_MAX) felix@6538: .addScalar("cross_section_id", StandardBasicTypes.INTEGER); felix@6538: felix@6538: sqlQuery felix@6538: .setInteger("river_id", getRiver().getId()) felix@6538: .setDouble("km", km); felix@6538: felix@6538: List results = sqlQuery.list(); felix@6538: felix@6852: if (results.size() >= 1) { felix@6852: Integer result = results.get(0); felix@6538: if (result == getId()) { felix@6538: return true; felix@6538: } felix@6538: } felix@6852: else { felix@6852: logger.warn("No CS found that could be master."); felix@6852: } felix@6852: felix@6852: // TODO If there is none, might need a fallback. felix@6852: // Formerly this was the most current CS (issue1157). felix@6852: felix@6538: return false; felix@6538: } sascha@1194: } sascha@1194: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :