teichmann@8049: /* Copyright (C) 2014 by Bundesanstalt für Gewässerkunde teichmann@8049: * Software engineering by Intevation GmbH teichmann@8049: * teichmann@8049: * This file is Free Software under the GNU AGPL (>=v3) teichmann@8049: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@8049: * documentation coming with Dive4Elements River for details. teichmann@8049: */ teichmann@8049: package org.dive4elements.river.artifacts.model.minfo; teichmann@8049: teichmann@8049: import java.util.List; teichmann@8049: import java.util.Set; teichmann@8055: import java.util.TreeSet; teichmann@8049: teichmann@8049: import org.dive4elements.river.artifacts.access.SedimentLoadAccess; teichmann@8049: import org.dive4elements.river.artifacts.model.Calculation; teichmann@8049: import org.dive4elements.river.artifacts.model.CalculationResult; teichmann@8053: import org.dive4elements.river.artifacts.model.RiverFactory; teichmann@8049: import org.apache.log4j.Logger; teichmann@8049: import org.dive4elements.river.artifacts.model.minfo.SedimentLoadData; teichmann@8049: import org.dive4elements.river.artifacts.model.minfo.SedimentLoadData.Value; teichmann@8049: import org.dive4elements.river.artifacts.model.minfo.SedimentLoadData.Station; teichmann@8055: import org.dive4elements.river.artifacts.model.minfo.SedimentLoadDataValueFilter.And; teichmann@8055: import org.dive4elements.river.artifacts.model.minfo.SedimentLoadDataValueFilter.IsEpoch; teichmann@8060: import org.dive4elements.river.artifacts.model.minfo.SedimentLoadDataValueFilter.IsOfficial; teichmann@8055: import org.dive4elements.river.artifacts.model.minfo.SedimentLoadDataValueFilter.Not; teichmann@8055: import org.dive4elements.river.artifacts.model.minfo.SedimentLoadDataValueFilter.TimeRangeIntersects; teichmann@8053: import org.dive4elements.river.model.River; teichmann@8053: import org.dive4elements.river.utils.DoubleUtil; teichmann@8049: teichmann@8049: public class SedimentLoadDataCalculation teichmann@8049: extends Calculation teichmann@8049: { teichmann@8049: private static final Logger log = Logger teichmann@8049: .getLogger(SedimentLoadDataCalculation.class); teichmann@8049: teichmann@8049: public static final int [] TOTAL_LOAD_FLYS = { teichmann@8049: SedimentLoadData.GF_COARSE, teichmann@8049: SedimentLoadData.GF_FINE_MIDDLE, teichmann@8049: SedimentLoadData.GF_SAND, teichmann@8049: SedimentLoadData.GF_SUSP_SEDIMENT teichmann@8049: }; teichmann@8049: teichmann@8049: public static final int [] BED_LOAD_FLYS = { teichmann@8049: SedimentLoadData.GF_COARSE, teichmann@8049: SedimentLoadData.GF_FINE_MIDDLE, teichmann@8049: SedimentLoadData.GF_SAND teichmann@8049: }; teichmann@8049: teichmann@8049: public static final int [] BED_LOAD_SUSP_SAND_FLYS = { teichmann@8049: SedimentLoadData.GF_COARSE, teichmann@8049: SedimentLoadData.GF_FINE_MIDDLE, teichmann@8049: SedimentLoadData.GF_SAND, teichmann@8049: SedimentLoadData.GF_SUSP_SAND teichmann@8049: }; teichmann@8049: teichmann@8050: public static final int [] TOTAL_LOAD_BFG = { teichmann@8050: SedimentLoadData.GF_TOTAL teichmann@8050: }; teichmann@8050: teichmann@8050: public static final int [] BED_LOAD_BFG = { teichmann@8050: SedimentLoadData.GF_BED_LOAD teichmann@8050: }; teichmann@8050: teichmann@8050: public static final int [] SUSPENDED_LOAD_BFG = { teichmann@8050: SedimentLoadData.GF_SUSPENDED_LOAD teichmann@8050: }; teichmann@8050: teichmann@8050: public static final int [] COARSE_FLYS = { teichmann@8050: SedimentLoadData.GF_COARSE teichmann@8050: }; teichmann@8050: teichmann@8050: public static final int [] FINE_MIDDLE_FLYS = { teichmann@8050: SedimentLoadData.GF_FINE_MIDDLE teichmann@8050: }; teichmann@8050: teichmann@8050: public static final int [] SAND_FLYS = { teichmann@8050: SedimentLoadData.GF_SAND teichmann@8050: }; teichmann@8050: teichmann@8050: public static final int [] SUSP_SAND_FLYS = { teichmann@8050: SedimentLoadData.GF_SUSP_SAND teichmann@8050: }; teichmann@8050: teichmann@8050: public static final int [] SUSP_SAND_BED_FLYS = { teichmann@8050: SedimentLoadData.GF_SUSP_SAND_BED teichmann@8050: }; teichmann@8050: teichmann@8050: public static final int [] SUSP_SEDIMENT_FLYS = { teichmann@8050: SedimentLoadData.GF_SUSP_SEDIMENT teichmann@8050: }; teichmann@8050: teichmann@8055: public static final class GrainFraction { teichmann@8055: private String description; teichmann@8055: private int [] grainFractions; teichmann@8055: teichmann@8055: public GrainFraction(String description, int [] grainFractions) { teichmann@8055: this.description = description; teichmann@8055: this.grainFractions = grainFractions; teichmann@8055: } teichmann@8055: public static final GrainFraction make(String description, int [] grainFractions) { teichmann@8055: return new GrainFraction(description, grainFractions); teichmann@8055: } teichmann@8055: teichmann@8055: public String getDescription() { teichmann@8055: return description; teichmann@8055: } teichmann@8055: teichmann@8055: public int [] getGrainFractions() { teichmann@8055: return grainFractions; teichmann@8055: } teichmann@8055: } // class GrainFraction teichmann@8055: teichmann@8055: public static final GrainFraction [] GRAIN_FRACTIONS = { teichmann@8055: // TODO: i18n teichmann@8055: GrainFraction.make("minfo.total.load.flys", TOTAL_LOAD_FLYS), teichmann@8055: GrainFraction.make("minfo.bed.load.flys", BED_LOAD_FLYS), teichmann@8055: GrainFraction.make("minfo.bed.load.susp.sand.flys", BED_LOAD_SUSP_SAND_FLYS), teichmann@8055: GrainFraction.make("minfo.total.load.bfg", TOTAL_LOAD_BFG), teichmann@8055: GrainFraction.make("minfo.bed.load.bfg", BED_LOAD_BFG), teichmann@8055: GrainFraction.make("minfo.suspended.load.bfg", SUSPENDED_LOAD_BFG), teichmann@8055: GrainFraction.make("minfo.coarse.flys", COARSE_FLYS), teichmann@8055: GrainFraction.make("minfo.fine.middle.flys", FINE_MIDDLE_FLYS), teichmann@8055: GrainFraction.make("minfo.sand.flys", SAND_FLYS) , teichmann@8055: GrainFraction.make("minfo.susp.sand.flys", SUSP_SAND_FLYS), teichmann@8055: GrainFraction.make("minfo.susp.sand.bed.flys", SUSP_SAND_BED_FLYS), teichmann@8055: GrainFraction.make("minfo.susp.sediment.flys", SUSP_SEDIMENT_FLYS), teichmann@8055: }; teichmann@8055: teichmann@8060: public static class Sum implements Value.Visitor { teichmann@8049: teichmann@8060: protected int n; teichmann@8060: protected double sum; teichmann@8049: teichmann@8049: public Sum() { teichmann@8049: } teichmann@8049: teichmann@8049: public double getSum() { teichmann@8060: return sum; teichmann@8049: } teichmann@8049: teichmann@8049: public int getN() { teichmann@8049: return n; teichmann@8049: } teichmann@8049: teichmann@8049: public void reset() { teichmann@8049: n = 0; teichmann@8049: sum = 0.0; teichmann@8049: } teichmann@8049: teichmann@8049: @Override teichmann@8049: public void visit(Value value) { teichmann@8049: sum += value.getValue(); teichmann@8049: ++n; teichmann@8049: } teichmann@8060: } // class Sum teichmann@8060: teichmann@8060: public static final class Avg extends Sum { teichmann@8060: public Avg() { teichmann@8060: } teichmann@8060: teichmann@8060: @Override teichmann@8060: public double getSum() { teichmann@8060: return n == 0 ? 0.0 : sum/(double)n; teichmann@8060: } teichmann@8060: } // class Sum teichmann@8049: teichmann@8049: teichmann@8052: private String river; teichmann@8052: private String yearEpoch; teichmann@8052: private String unit; teichmann@8055: private int [][] epochs; teichmann@8055: private int [] years; teichmann@8052: private double from; teichmann@8052: private double to; teichmann@8052: teichmann@8052: teichmann@8049: public SedimentLoadDataCalculation() { teichmann@8049: } teichmann@8049: teichmann@8049: public CalculationResult calculate(SedimentLoadAccess access) { teichmann@8049: log.info("SedimentLoadDataCalculation.calculate"); teichmann@8052: teichmann@8052: String river = access.getRiverName(); teichmann@8052: String yearEpoch = access.getYearEpoch(); teichmann@8052: String unit = access.getUnit(); teichmann@8052: teichmann@8055: int [] years = null; teichmann@8055: int [][] epochs = null; teichmann@8052: teichmann@8057: double from = access.getLowerKM(); teichmann@8057: double to = access.getUpperKM(); teichmann@8052: teichmann@8052: if (yearEpoch.equals("year")) { teichmann@8055: years = access.getPeriod(); teichmann@8052: } teichmann@8052: else if (yearEpoch.equals("epoch") || yearEpoch.equals("off_epoch")) { teichmann@8055: epochs = access.getEpochs(); teichmann@8052: } teichmann@8052: else { teichmann@8052: addProblem("minfo.missing.year_epoch"); teichmann@8052: } teichmann@8052: teichmann@8052: if (river == null) { teichmann@8052: // TODO: i18n teichmann@8052: addProblem("minfo.missing.river"); teichmann@8052: } teichmann@8052: teichmann@8055: if (years == null && epochs == null) { teichmann@8052: addProblem("minfo.missing.time"); teichmann@8052: } teichmann@8052: teichmann@8052: if (!hasProblems()) { teichmann@8052: this.river = river; teichmann@8052: this.yearEpoch = yearEpoch; teichmann@8052: this.unit = unit; teichmann@8055: this.years = years; teichmann@8055: this.epochs = epochs; teichmann@8052: this.from = from; teichmann@8052: this.to = to; teichmann@8052: return internalCalculate(); teichmann@8052: } teichmann@8052: teichmann@8055: return error(null); teichmann@8055: } teichmann@8055: teichmann@8055: private CalculationResult error(String msg) { teichmann@8055: if (msg != null) addProblem(msg); teichmann@8053: return new CalculationResult(this); teichmann@8052: } teichmann@8052: teichmann@8052: private CalculationResult internalCalculate() { teichmann@8053: if ("year".equals(yearEpoch)) return calculateYears(); teichmann@8053: if ("epoch".equals(yearEpoch)) return calculateEpochs(); teichmann@8053: if ("off_epoch".equals(yearEpoch)) return calculateOffEpochs(); teichmann@8053: teichmann@8053: // TODO: i18n teichmann@8055: return error("minfo.sediment.load.unknown.calc.mode"); teichmann@8053: } teichmann@8053: teichmann@8053: private CalculationResult calculateYears() { teichmann@8055: SedimentLoadData sld = teichmann@8055: SedimentLoadDataFactory.INSTANCE.getSedimentLoadData(river); teichmann@8055: if (sld == null) { teichmann@8055: // TODO: i18n teichmann@8055: return error("minfo.sediment.load.no.data"); teichmann@8055: } teichmann@8055: teichmann@8057: boolean isKmUp = isKmUp(); teichmann@8055: Set missingFractions = new TreeSet(); teichmann@8055: teichmann@8055: Not notEpochs = new Not(IsEpoch.INSTANCE); teichmann@8055: teichmann@8055: Sum sum = new Sum(); teichmann@8055: teichmann@8055: for (int year: years) { teichmann@8055: Value.Filter filter = new And() teichmann@8055: .add(notEpochs) teichmann@8055: .add(new TimeRangeIntersects(year)); teichmann@8055: teichmann@8055: for (GrainFraction gf: GRAIN_FRACTIONS) { teichmann@8055: double [][] result = sum( teichmann@8055: sld, gf.getGrainFractions(), filter, sum, isKmUp, teichmann@8055: missingFractions); teichmann@8055: teichmann@8055: if (result[0].length == 0 || DoubleUtil.isNaN(result[1])) { teichmann@8055: // TODO: resolve i18n teichmann@8055: addProblem("minfo.sediment.load.no.fractions", teichmann@8055: gf.getDescription()); teichmann@8055: continue; teichmann@8055: } teichmann@8055: // TODO: Generate result data set for calculation. teichmann@8057: // TODO: Optionally transform units. teichmann@8055: } teichmann@8055: } teichmann@8055: // TODO: Generate messages for missing fractions. teichmann@8057: // TODO: Bundle sub results. teichmann@8049: return null; teichmann@8049: } teichmann@8049: teichmann@8053: private CalculationResult calculateEpochs() { teichmann@8057: SedimentLoadData sld = teichmann@8057: SedimentLoadDataFactory.INSTANCE.getSedimentLoadData(river); teichmann@8057: if (sld == null) { teichmann@8057: // TODO: i18n teichmann@8057: return error("minfo.sediment.load.no.data"); teichmann@8057: } teichmann@8057: teichmann@8057: boolean isKmUp = isKmUp(); teichmann@8057: Set missingFractions = new TreeSet(); teichmann@8057: teichmann@8060: // They are not epochs, they are single years! teichmann@8060: Not notEpochs = new Not(IsEpoch.INSTANCE); teichmann@8060: teichmann@8057: for (int [] epoch: epochs) { teichmann@8057: Value.Filter filter = new And() teichmann@8060: .add(notEpochs) teichmann@8057: .add(new TimeRangeIntersects(epoch[0], epoch[1])); teichmann@8057: teichmann@8060: Avg avg = new Avg(); teichmann@8057: teichmann@8060: for (GrainFraction gf: GRAIN_FRACTIONS) { teichmann@8060: double [][] result = sum( teichmann@8060: sld, gf.getGrainFractions(), filter, avg, isKmUp, teichmann@8060: missingFractions); teichmann@8060: teichmann@8060: if (result[0].length == 0 || DoubleUtil.isNaN(result[1])) { teichmann@8060: // TODO: resolve i18n teichmann@8060: addProblem("minfo.sediment.load.no.fractions", teichmann@8060: gf.getDescription()); teichmann@8060: continue; teichmann@8060: } teichmann@8060: // TODO: Generate result data set for calculation. teichmann@8060: // TODO: Optionally transform units. teichmann@8060: } teichmann@8060: } teichmann@8060: // TODO: Generate messages for missing fractions. teichmann@8060: // TODO: Bundle sub results. teichmann@8060: return null; teichmann@8060: } teichmann@8060: teichmann@8060: private CalculationResult calculateOffEpochs() { teichmann@8060: SedimentLoadData sld = teichmann@8060: SedimentLoadDataFactory.INSTANCE.getSedimentLoadData(river); teichmann@8060: if (sld == null) { teichmann@8060: // TODO: i18n teichmann@8060: return error("minfo.sediment.load.no.data"); teichmann@8060: } teichmann@8060: teichmann@8060: boolean isKmUp = isKmUp(); teichmann@8060: Set missingFractions = new TreeSet(); teichmann@8060: teichmann@8060: for (int [] epoch: epochs) { teichmann@8060: Value.Filter filter = new And() teichmann@8060: .add(IsOfficial.INSTANCE) teichmann@8060: .add(new TimeRangeIntersects(epoch[0], epoch[1])); teichmann@8060: teichmann@8060: Sum sum = new Sum(); teichmann@8057: teichmann@8057: for (GrainFraction gf: GRAIN_FRACTIONS) { teichmann@8057: double [][] result = sum( teichmann@8057: sld, gf.getGrainFractions(), filter, sum, isKmUp, teichmann@8057: missingFractions); teichmann@8057: teichmann@8057: if (result[0].length == 0 || DoubleUtil.isNaN(result[1])) { teichmann@8057: // TODO: resolve i18n teichmann@8057: addProblem("minfo.sediment.load.no.fractions", teichmann@8057: gf.getDescription()); teichmann@8057: continue; teichmann@8057: } teichmann@8057: // TODO: Generate result data set for calculation. teichmann@8057: // TODO: Optionally transform units. teichmann@8057: } teichmann@8057: } teichmann@8057: // TODO: Generate messages for missing fractions. teichmann@8057: // TODO: Bundle sub results. teichmann@8053: // TODO: Implement me! teichmann@8053: return null; teichmann@8053: } teichmann@8053: teichmann@8053: /** Figure out flow direction of river. */ teichmann@8053: private boolean isKmUp() { teichmann@8053: River r = RiverFactory.getRiver(river); teichmann@8053: if (r == null) { teichmann@8053: addProblem("minfo.missing.river"); teichmann@8053: return true; teichmann@8049: } teichmann@8053: return r.getKmUp(); teichmann@8049: } teichmann@8049: teichmann@8049: public double[][] sum( teichmann@8049: SedimentLoadData sld, teichmann@8049: int [] grainFractions, teichmann@8049: Value.Filter filter, teichmann@8049: Sum sum, teichmann@8049: boolean isKMUp, teichmann@8049: Set missingFractions teichmann@8049: ) { teichmann@8049: List stations = sld.findStations(from, to); teichmann@8049: teichmann@8049: double [] values = new double[grainFractions.length]; teichmann@8049: teichmann@8049: double [][] result = new double[2][stations.size()]; teichmann@8049: teichmann@8049: for (int j = 0, S = stations.size(); j < S; ++j) { teichmann@8049: Station station = stations.get(j); teichmann@8049: for (int i = 0; i < grainFractions.length; ++i) { teichmann@8049: int gf = grainFractions[i]; teichmann@8049: sum.reset(); teichmann@8049: station.filterGrainFraction(gf, filter, sum); teichmann@8049: if (sum.getN() == 0) { // No values found teichmann@8049: int msType = SedimentLoadData.measurementStationType(gf); teichmann@8049: // Station of right fraction type already? No: take previous. teichmann@8049: if (!station.isType(msType)) { teichmann@8049: Station prev = station.prevByType(msType, isKMUp); teichmann@8049: if (prev != null) { teichmann@8049: prev.filterGrainFraction(gf, filter, sum); teichmann@8049: } teichmann@8049: } teichmann@8049: } teichmann@8049: teichmann@8049: if (sum.getN() == 0) { teichmann@8049: missingFractions.add(gf); teichmann@8049: values[i] = Double.NaN; teichmann@8049: } else { teichmann@8049: values[i] = sum.getSum(); teichmann@8049: } teichmann@8049: } teichmann@8049: result[0][j] = station.getStation(); teichmann@8053: result[1][j] = DoubleUtil.sum(values); teichmann@8049: } teichmann@8049: teichmann@8049: // TODO: Handle 'virtual' measument stations 'from' and 'to'. teichmann@8049: teichmann@8049: return result; teichmann@8049: } teichmann@8049: } teichmann@8049: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :