Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java @ 9613:f2473dc34535
Nachtrag Pos. 19: Q calculation with historical discharge tables instead of master discharge table
author | mschaefer |
---|---|
date | Tue, 08 Oct 2019 15:03:24 +0200 |
parents | 8b457ca70512 |
children | 1d4262a68f1f |
comparison
equal
deleted
inserted
replaced
9602:6b2496d71936 | 9613:f2473dc34535 |
---|---|
9 */ | 9 */ |
10 package org.dive4elements.river.artifacts.sinfo.collision; | 10 package org.dive4elements.river.artifacts.sinfo.collision; |
11 | 11 |
12 import java.util.ArrayList; | 12 import java.util.ArrayList; |
13 import java.util.Collection; | 13 import java.util.Collection; |
14 import java.util.Date; | |
14 import java.util.HashMap; | 15 import java.util.HashMap; |
15 import java.util.List; | 16 import java.util.List; |
16 import java.util.Map; | 17 import java.util.Map; |
18 import java.util.Map.Entry; | |
17 import java.util.NavigableSet; | 19 import java.util.NavigableSet; |
20 import java.util.TreeMap; | |
18 import java.util.TreeSet; | 21 import java.util.TreeSet; |
19 | 22 |
20 import org.apache.commons.lang.math.DoubleRange; | 23 import org.apache.commons.lang.math.DoubleRange; |
21 import org.dive4elements.artifacts.CallContext; | 24 import org.dive4elements.artifacts.CallContext; |
22 import org.dive4elements.river.artifacts.common.GeneralResultType; | 25 import org.dive4elements.river.artifacts.common.GeneralResultType; |
30 import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueFinder; | 33 import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueFinder; |
31 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; | 34 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; |
32 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils; | 35 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils; |
33 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; | 36 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; |
34 import org.dive4elements.river.backend.utils.DateUtil; | 37 import org.dive4elements.river.backend.utils.DateUtil; |
38 import org.dive4elements.river.model.DischargeTable; | |
39 import org.dive4elements.river.model.Gauge; | |
35 import org.dive4elements.river.model.MainValueType.MainValueTypeKey; | 40 import org.dive4elements.river.model.MainValueType.MainValueTypeKey; |
36 import org.dive4elements.river.model.River; | 41 import org.dive4elements.river.model.River; |
37 import org.dive4elements.river.model.sinfo.CollisionAggregateValue; | 42 import org.dive4elements.river.model.sinfo.CollisionAggregateValue; |
38 import org.dive4elements.river.model.sinfo.CollisionValue; | 43 import org.dive4elements.river.model.sinfo.CollisionValue; |
39 | 44 |
41 | 46 |
42 // private static Logger log = Logger.getLogger(FloodDurationCalculation.class); | 47 // private static Logger log = Logger.getLogger(FloodDurationCalculation.class); |
43 | 48 |
44 private final CallContext context; | 49 private final CallContext context; |
45 | 50 |
51 private CollisionAccess access; | |
52 private River river; | |
53 private List<DateRange> years; | |
54 private Calculation problems; | |
55 private int qfinderProblemCount; | |
56 | |
46 public CollisionCalculation(final CallContext context) { | 57 public CollisionCalculation(final CallContext context) { |
47 this.context = context; | 58 this.context = context; |
48 } | 59 } |
49 | 60 |
50 public CalculationResult calculate(final SINFOArtifact sinfo) { | 61 public CalculationResult calculate(final SINFOArtifact sinfo) { |
51 | 62 |
52 final String user = CalculationUtils.findArtifactUser(this.context, sinfo); | 63 final String user = CalculationUtils.findArtifactUser(this.context, sinfo); |
53 | 64 |
54 // access input data | 65 // access input data |
55 final CollisionAccess access = new CollisionAccess(sinfo); | 66 this.access = new CollisionAccess(sinfo); |
56 final River river = access.getRiver(); | 67 this.river = this.access.getRiver(); |
57 final RiverInfo riverInfo = new RiverInfo(river); | 68 final RiverInfo riverInfo = new RiverInfo(this.river); |
58 | 69 final DoubleRange calcRange = this.access.getRange(); |
59 final DoubleRange calcRange = access.getRange(); | |
60 | 70 |
61 // calculate results for each year or epoch | 71 // calculate results for each year or epoch |
62 final Calculation problems = new Calculation(); | 72 this.problems = new Calculation(); |
63 | 73 this.qfinderProblemCount = 0; |
64 final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); | 74 final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); |
65 | 75 |
66 final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, access.getYearsHeader()); | 76 final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, this.access.getYearsHeader()); |
67 | 77 |
68 final Collection<ResultRow> overViewRows = new ArrayList<>(); | 78 final Collection<ResultRow> overViewRows = new ArrayList<>(); |
69 | 79 |
70 final List<DateRange> years = new ArrayList<>(); | 80 this.years = new ArrayList<>(); |
71 final NavigableSet<Integer> detailYears = new TreeSet<>(); | 81 final NavigableSet<Integer> detailYears = new TreeSet<>(); |
72 if (access.getYears() != null) { | 82 if (this.access.getYears() != null) { |
73 for (final int year : access.getYears()) { | 83 for (final int year : this.access.getYears()) { |
74 calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), year, year, false); | 84 calculateOverview(overViewRows, year, year, false); |
75 years.add(new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))); | 85 this.years.add(new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))); |
76 detailYears.add(Integer.valueOf(year)); | 86 detailYears.add(Integer.valueOf(year)); |
77 } | 87 } |
78 } else { | 88 } else { |
79 for (final DateRange dr : access.getEpochs()) { | 89 for (final DateRange dr : this.access.getEpochs()) { |
80 calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), dr.getFromYear(), dr.getToYear(), true); | 90 calculateOverview(overViewRows, dr.getFromYear(), dr.getToYear(), true); |
81 years.add(dr); | 91 this.years.add(dr); |
82 detailYearsAdd(detailYears, dr); | 92 detailYearsAdd(detailYears, dr); |
83 } | 93 } |
84 } | 94 } |
85 final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(access.getYearsHeader(), (access.getYears() == null), years, | 95 final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(this.access.getYearsHeader(), (this.access.getYears() == null), |
86 overViewRows); | 96 this.years, overViewRows); |
87 results.addResult(overviewResult, problems); | 97 results.addResult(overviewResult, this.problems); |
88 | 98 |
89 // calculate secondary results for each year | 99 // calculate secondary results for each year |
90 final Map<String, GaugeDischargeValuesFinder> qFinders = new HashMap<>(); | 100 final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders = new HashMap<>(); |
91 final Map<String, GaugeMainValueFinder> zoneFinders = new HashMap<>(); | 101 final Map<String, GaugeMainValueFinder> zoneFinders = new HashMap<>(); |
92 final Collection<ResultRow> detailsRows = new ArrayList<>(); | 102 final Collection<ResultRow> detailsRows = new ArrayList<>(); |
93 for (final Integer year : detailYears) | 103 for (final Integer year : detailYears) |
94 calculateDetails(detailsRows, river, access.getLowerKm(), access.getUpperKm(), year, qFinders, zoneFinders, problems); | 104 calculateDetails(detailsRows, year, qFinders, zoneFinders); |
95 final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows); | 105 final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows); |
96 results.addResult(detailResult, problems); | 106 results.addResult(detailResult, this.problems); |
97 | 107 |
98 return new CalculationResult(results, problems); | 108 return new CalculationResult(results, this.problems); |
99 } | 109 } |
100 | 110 |
101 /** | 111 /** |
102 * Adds all years of an epoch to a set | 112 * Adds all years of an epoch to a set |
103 */ | 113 */ |
108 | 118 |
109 /** | 119 /** |
110 * Calculates the collision counts for a km range of a river and a year or year range (epoch), | 120 * Calculates the collision counts for a km range of a river and a year or year range (epoch), |
111 * and adds them to a ResultRow collection | 121 * and adds them to a ResultRow collection |
112 */ | 122 */ |
113 private void calculateOverview(final Collection<ResultRow> rows, final River river, final double fromKm, final double toKm, final int fromYear, | 123 private void calculateOverview(final Collection<ResultRow> rows, final int fromYear, final int toYear, final boolean isEpoch) { |
114 final int toYear, final boolean isEpoch) { | 124 for (final CollisionAggregateValue aggregate : CollisionAggregateValue.getValuesByKm(this.river, this.access.getLowerKm(), this.access.getUpperKm(), |
115 for (final CollisionAggregateValue aggregate : CollisionAggregateValue.getValuesByKm(river, fromKm, toKm, fromYear, toYear)) { | 125 fromYear, toYear)) { |
116 rows.add(ResultRow.create().putValue(GeneralResultType.station, aggregate.getStation()) | 126 rows.add(ResultRow.create().putValue(GeneralResultType.station, aggregate.getStation()) |
117 .putValue(SInfoResultType.years, yearsToString(isEpoch, fromYear, toYear)).putValue(SInfoResultType.collisionCount, aggregate.getCount())); | 127 .putValue(SInfoResultType.years, yearsToString(isEpoch, fromYear, toYear)).putValue(SInfoResultType.collisionCount, aggregate.getCount())); |
118 } | 128 } |
119 } | 129 } |
120 | 130 |
133 } | 143 } |
134 | 144 |
135 /** | 145 /** |
136 * Calculates the collision details for a km range of a river and a year, and adds them to a ResultRow collection | 146 * Calculates the collision details for a km range of a river and a year, and adds them to a ResultRow collection |
137 */ | 147 */ |
138 private void calculateDetails(final Collection<ResultRow> rows, final River river, final double fromKm, final double toKm, final int year, | 148 private void calculateDetails(final Collection<ResultRow> rows, final int year, final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, |
139 final Map<String, GaugeDischargeValuesFinder> qFinders, final Map<String, GaugeMainValueFinder> zoneFinders, final Calculation problems) { | 149 final Map<String, GaugeMainValueFinder> zoneFinders) { |
140 | 150 |
141 for (final CollisionValue collision : CollisionValue.getValues(river, fromKm, toKm, DateUtil.getStartDateFromYear(year), | 151 for (final CollisionValue collision : CollisionValue.getValues(this.river, this.access.getLowerKm(), this.access.getUpperKm(), |
142 DateUtil.getEndDateFromYear(year))) { | 152 DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))) { |
143 final String gaugeName = collision.getGaugeName(); | 153 final String gaugeName = collision.getGaugeName(); |
144 final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), river, problems); | 154 final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), collision.getEventDate()); |
145 final double qOut = Double.isInfinite(q) ? Double.NaN : q; | 155 final double qOut = Double.isInfinite(q) ? Double.NaN : q; |
146 final String zone = getZone(zoneFinders, gaugeName, q, river, problems); | 156 final String zone = getZone(zoneFinders, gaugeName, q); |
147 rows.add(ResultRow.create().putValue(GeneralResultType.station, collision.getStation()) | 157 rows.add(ResultRow.create().putValue(GeneralResultType.station, collision.getStation()) |
148 .putValue(GeneralResultType.dateShort, collision.getEventDate()).putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW()) | 158 .putValue(GeneralResultType.dateShort, collision.getEventDate()).putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW()) |
149 .putValue(GeneralResultType.gaugeLabel, gaugeName).putValue(SInfoResultType.dischargeLong, qOut) | 159 .putValue(GeneralResultType.gaugeLabel, gaugeName).putValue(SInfoResultType.dischargeLong, qOut) |
150 .putValue(SInfoResultType.dischargeZone, zone)); | 160 .putValue(SInfoResultType.dischargeZone, zone)); |
151 } | 161 } |
152 } | 162 } |
153 | 163 |
154 /** | 164 /** |
155 * Gets the discharge of a gauge and a W | 165 * Gets the discharge of a gauge and a W |
156 */ | 166 */ |
157 private double getQ(final Map<String, GaugeDischargeValuesFinder> qFinders, final String gaugeName, final double w, final River river, | 167 private double getQ(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName, final double w, final Date when) { |
158 final Calculation problems) { | |
159 // Find the gauge and load its discharge table, if not already in the map | 168 // Find the gauge and load its discharge table, if not already in the map |
160 final String gnKey = gaugeName.toLowerCase(); | 169 final String gnKey = gaugeName.toLowerCase(); |
161 if (!qFinders.containsKey(gnKey)) | 170 if (!qFinders.containsKey(gnKey)) |
162 qFinders.put(gnKey, GaugeDischargeValuesFinder.loadValues(river, gaugeName, problems)); | 171 addQFinders(qFinders, gaugeName); |
163 // Interpolate W. | 172 // Interpolate W. |
164 if (qFinders.get(gnKey) == null) | 173 final GaugeDischargeValuesFinder qFinder = getQFinder(qFinders.get(gnKey), when); |
174 if (qFinder == null) { | |
175 this.qfinderProblemCount++; | |
176 if (this.qfinderProblemCount == 1) | |
177 this.problems.addProblem("gauge_discharge_table.missing", gaugeName); | |
165 return Double.NaN; | 178 return Double.NaN; |
166 return qFinders.get(gnKey).getDischarge(w); | 179 } |
180 return qFinder.getDischarge(w); | |
181 } | |
182 | |
183 /** | |
184 * Add the discharge finders for a gauge and the active time period | |
185 */ | |
186 private void addQFinders(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName) { | |
187 final String gnKey = gaugeName.toLowerCase(); | |
188 final Gauge gauge = this.river.determineGaugeByName(gaugeName); | |
189 if (gauge == null) { | |
190 qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>()); | |
191 return; | |
192 } | |
193 final List<DischargeTable> qtables = DischargeTable.fetchHistoricalDischargeTables(gauge, this.years.get(0).getFrom(), | |
194 this.years.get(this.years.size() - 1).getTo()); | |
195 qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>()); | |
196 for (final DischargeTable qtable : qtables) | |
197 qFinders.get(gnKey).put(qtable.getTimeInterval().getStartTime(), | |
198 GaugeDischargeValuesFinder.loadValues(qtable, this.river, gaugeName, this.problems)); | |
199 } | |
200 | |
201 /** | |
202 * Searches a q values finder map for a date time | |
203 */ | |
204 private GaugeDischargeValuesFinder getQFinder(final TreeMap<Date, GaugeDischargeValuesFinder> qFinders, final Date when) { | |
205 if (qFinders.containsKey(when)) | |
206 return qFinders.get(when); | |
207 final Entry<Date, GaugeDischargeValuesFinder> found = qFinders.floorEntry(when); | |
208 if (found == null) | |
209 return null; | |
210 if ((found.getValue().getEndTime() != null) && found.getValue().getEndTime().before(when)) | |
211 return null; | |
212 return found.getValue(); | |
167 } | 213 } |
168 | 214 |
169 /** | 215 /** |
170 * Gets the main value zone name of a gauge and a Q | 216 * Gets the main value zone name of a gauge and a Q |
171 */ | 217 */ |
172 private String getZone(final Map<String, GaugeMainValueFinder> zoneFinders, final String gaugeName, final double q, final River river, | 218 private String getZone(final Map<String, GaugeMainValueFinder> zoneFinders, final String gaugeName, final double q) { |
173 final Calculation problems) { | |
174 // Find the gauge and load its main value list, if not already in the map | 219 // Find the gauge and load its main value list, if not already in the map |
175 final String gnKey = gaugeName.toLowerCase(); | 220 final String gnKey = gaugeName.toLowerCase(); |
176 if (!zoneFinders.containsKey(gnKey)) | 221 if (!zoneFinders.containsKey(gnKey)) |
177 zoneFinders.put(gnKey, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, river, gaugeName, problems, "GLQ")); | 222 zoneFinders.put(gnKey, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, this.river, gaugeName, this.problems, "GLQ")); |
178 // Build the zone name | 223 // Build the zone name |
179 if (zoneFinders.get(gnKey) == null) | 224 if (zoneFinders.get(gnKey) == null) |
180 return ""; | 225 return ""; |
181 return zoneFinders.get(gnKey).findZoneName(q); | 226 return zoneFinders.get(gnKey).findZoneName(q); |
182 } | 227 } |