Mercurial > dive4elements > river
view artifacts/src/main/java/org/dive4elements/river/artifacts/services/DynamicMainValuesService.java @ 9415:9744ce3c3853
Rework of fixanalysis computation and dWt and WQ facets. Got rid of strange remapping and bitshifting code by explicitely saving the column information and using it in the facets.
The facets also put the valid station range into their xml-metadata
author | gernotbelger |
---|---|
date | Thu, 16 Aug 2018 16:27:53 +0200 |
parents | bc9a45d2b1fa |
children | 4cccbd32b680 |
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.artifacts.services; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import org.dive4elements.artifacts.common.utils.DateUtils; import org.dive4elements.river.backend.SessionHolder; import org.dive4elements.river.model.Gauge; import org.dive4elements.river.model.MainValue; import org.dive4elements.river.model.MainValueType; import org.dive4elements.river.model.MainValueType.MainValueTypeKey; import org.dive4elements.river.model.NamedMainValue; import org.dive4elements.river.model.OfficialLine; import org.dive4elements.river.model.River; import org.dive4elements.river.model.TimeInterval; import org.dive4elements.river.model.sinfo.DailyDischargeValue; import org.dive4elements.river.model.sinfo.DailyDischargeValue.OrderByField; import org.dive4elements.river.utils.DoubleUtil; import org.hibernate.Session; import gnu.trove.TDoubleArrayList; /** * This service returns the main values of a river's gauge based on the start * and end point of the river. * * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> */ public class DynamicMainValuesService extends AbstractDynamicMainValuesService { private static final long serialVersionUID = 1L; /** * Computes a gauge's main values for a period of time based on its daily discharges stored in the database */ @Override protected List<MainValue> getMainValues(final River river, final Gauge gauge, final Date startTime, final Date endTime) throws MainValuesServiceException { final List<MainValue> mainValues = new ArrayList<>(); computeMainDischargeValues(gauge, startTime, endTime, mainValues); return mainValues; } /** * Computes mnq, mq, mhq, hq5 and q(d=0..364) and adds them to a MainValue list */ private void computeMainDischargeValues(final Gauge gauge, final Date startTime, final Date endTime, final List<MainValue> mainValues) throws MainValuesServiceException { // Query the gauge's daily Q values final List<DailyDischargeValue> qdvs = DailyDischargeValue.getValues(gauge, startTime, endTime, OrderByField.DAY); if (qdvs.isEmpty()) throw new MainValuesServiceException("no daily discharge values for gauge " + gauge.getName() + " in the requested time period"); // return; // Build yearly aggregates final TDoubleArrayList mnqs = new TDoubleArrayList(); final TDoubleArrayList mqs = new TDoubleArrayList(); int mqcnt = 0; final TDoubleArrayList mhqs = new TDoubleArrayList(); for (int i = 0, j = 0; i <= qdvs.size() - 1; i++) { final DailyDischargeValue qdv = qdvs.get(i); if ((i >= 1) && isSameQYear(qdv.getDay(), qdvs.get(i - 1).getDay())) { // Continue aggregating the values of the current year mnqs.set(j, Math.min(mnqs.get(j), qdv.getDischarge().doubleValue())); mhqs.set(j, Math.max(mhqs.get(j), qdv.getDischarge().doubleValue())); mqs.set(j, mqs.get(j) + qdv.getDischarge().doubleValue()); mqcnt++; if (i == qdvs.size() - 1) mqs.set(j, mqs.get(j) / mqcnt); } else { // Complete mq aggregation if (mqcnt >= 1) { mqs.set(j, mqs.get(j) / mqcnt); j++; } // Start next year mnqs.add(qdv.getDischarge().doubleValue()); mhqs.add(qdv.getDischarge().doubleValue()); mqs.add(qdv.getDischarge().doubleValue()); mqcnt = 1; } } // Compute arithmetic means of the yearly values final Session session = SessionHolder.HOLDER.get(); final TimeInterval timeperiod = new TimeInterval(startTime, endTime); final double mnq = DoubleUtil.sum(mnqs.toNativeArray()) / mnqs.size(); mainValues.add(createMainValue(gauge, fetchNamedQMainValue("MNQ", session), mnq, timeperiod)); final double mq = DoubleUtil.sum(mqs.toNativeArray()) / mqs.size(); mainValues.add(createMainValue(gauge, fetchNamedQMainValue("MQ", session), mq, timeperiod)); final double mhq = DoubleUtil.sum(mhqs.toNativeArray()) / mhqs.size(); mainValues.add(createMainValue(gauge, fetchNamedQMainValue("MHQ", session), mhq, timeperiod)); // Compute hq5 - obsolete // mhqs.sort(); // final double hq5 = mhqs.get((int) Math.ceil(4 * mhqs.size() / 5)); // mainValues.add(createMainValue(gauge, fetchNamedQMainValue("HQ5", session), hq5, timeperiod)); // Add HSQ-II from the gauge's main values final MainValue hsq2 = fetchHsqII(gauge, session); if (hsq2 != null) mainValues.add(hsq2); // Query the gauge's daily Q values and build a list sorted by ascending Q final TDoubleArrayList qs = new TDoubleArrayList(); for (final DailyDischargeValue qdv : qdvs) qs.add(qdv.getDischarge().doubleValue()); qs.sort(); // Step through the sorted Q list and get the duration discharges final int yearCnt = DateUtils.getYearFromDate(endTime) - DateUtils.getYearFromDate(startTime) + 1; double glq20 = Double.NaN; for (int i = 0, k = 0; (i <= 364) && (k <= qs.size() - 1); i++, k += yearCnt) { final NamedMainValue nmv = fetchNamedQMainValue(i, session, mainValues.get(0).getMainValue().getType()); if (nmv != null) { final double q = getDurationQ(qs, k); mainValues.add(createMainValue(gauge, nmv, q, timeperiod)); if (i == 20) glq20 = q; } } mainValues.add(createMainValue(gauge, fetchNamedQMainValue("GlQ", session), glq20, timeperiod)); } /** * Checks year equality of two dates (calendar year) */ private boolean isSameQYear(final Date a, final Date b) { final Calendar ca = Calendar.getInstance(); ca.setTime(a); final Calendar cb = Calendar.getInstance(); cb.setTime(b); return (ca.get(Calendar.YEAR) == cb.get(Calendar.YEAR)); } /** * Fetches the gauge's HSQ-II from the database, or returns null */ private MainValue fetchHsqII(final Gauge gauge, final Session session) { final NamedMainValue nmv = NamedMainValue.fetchByNameAndType("HSQ-II", MainValueTypeKey.UNKNOWN.getName(), session); if (nmv == null) return null; final List<MainValue> mvs = gauge.getMainValues(); for (final MainValue mv : mvs) { if (mv.getMainValue().getId() == nmv.getId()) return mv; } return null; } /** * Fetches a named main Q value from the database, if existing */ private NamedMainValue fetchNamedQMainValue(final String name, final Session session) { final NamedMainValue nmv = NamedMainValue.fetchByNameAndType(name, MainValueTypeKey.Q.getName(), session); if (nmv != null) nmv.setOfficialLines(new ArrayList<OfficialLine>()); return nmv; } /** * Fetches a named main Q(duration) value from the database, if existing */ private NamedMainValue fetchNamedQMainValue(final int days, final Session session, final MainValueType qType) { final NamedMainValue nmv = NamedMainValue.fetchByNameAndType(Integer.toString(days), MainValueTypeKey.DURATION.getName(), session); // final NamedMainValue nmv = new NamedMainValue(Integer.toString(days), qType); if (nmv != null) nmv.setOfficialLines(new ArrayList<OfficialLine>()); return nmv; } /** * Creates a main value for a main value name */ private MainValue createMainValue(final Gauge gauge, final NamedMainValue nmv, final double value, final TimeInterval timeperiod) { return new MainValue(gauge, nmv, BigDecimal.valueOf(value).setScale(0, RoundingMode.HALF_EVEN), timeperiod); // TODO Scale per Formatter-Konstante o.ä. } /** * Gets the q from a list at a list position, or the next larger q if the immediate successors are equal */ private double getDurationQ(final TDoubleArrayList qs, final int i) { if (i == 0) return qs.getQuick(0); for (int j = i; j <= qs.size() - 1; j++) { if (qs.getQuick(j) > qs.getQuick(j - 1) + 0.001) return qs.getQuick(j); } // Identical values at end of list: increment q on third significant digit final MathContext mc = new MathContext(3, RoundingMode.FLOOR); BigDecimal qplus = BigDecimal.valueOf(qs.getQuick(qs.size() - 1)); qplus = qplus.round(mc).add(BigDecimal.ONE.scaleByPowerOfTen(-qplus.scale())); return qplus.doubleValue(); } }