comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java @ 9617:1d4262a68f1f

#12 Minuend/Subtrahend + MergeConflict #19 CollisionCalculation
author dnt_bjoernsen <d.tironi@bjoernsen.de>
date Thu, 10 Oct 2019 15:29:02 +0200
parents f2473dc34535
children
comparison
equal deleted inserted replaced
9616:cedcee24a21a 9617:1d4262a68f1f
1 /* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde 1 /* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
2 * Software engineering by 2 * Software engineering by
3 * Björnsen Beratende Ingenieure GmbH 3 * Björnsen Beratende Ingenieure GmbH
4 * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt 4 * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
5 * 5 *
6 * This file is Free Software under the GNU AGPL (>=v3) 6 * This file is Free Software under the GNU AGPL (>=v3)
7 * and comes with ABSOLUTELY NO WARRANTY! Check out the 7 * and comes with ABSOLUTELY NO WARRANTY! Check out the
8 * documentation coming with Dive4Elements River for details. 8 * documentation coming with Dive4Elements River for details.
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.Collections;
14 import java.util.Date; 15 import java.util.Date;
15 import java.util.HashMap; 16 import java.util.HashMap;
16 import java.util.List; 17 import java.util.List;
17 import java.util.Map; 18 import java.util.Map;
18 import java.util.Map.Entry; 19 import java.util.Map.Entry;
46 47
47 // private static Logger log = Logger.getLogger(FloodDurationCalculation.class); 48 // private static Logger log = Logger.getLogger(FloodDurationCalculation.class);
48 49
49 private final CallContext context; 50 private final CallContext context;
50 51
51 private CollisionAccess access;
52 private River river;
53 private List<DateRange> years;
54 private Calculation problems;
55 private int qfinderProblemCount;
56
57 public CollisionCalculation(final CallContext context) { 52 public CollisionCalculation(final CallContext context) {
58 this.context = context; 53 this.context = context;
59 } 54 }
60 55
61 public CalculationResult calculate(final SINFOArtifact sinfo) { 56 public CalculationResult calculate(final SINFOArtifact sinfo) {
62 57
63 final String user = CalculationUtils.findArtifactUser(this.context, sinfo); 58 final String user = CalculationUtils.findArtifactUser(this.context, sinfo);
64 59
60 final int qfinderProblemCount = 0;
61
65 // access input data 62 // access input data
66 this.access = new CollisionAccess(sinfo); 63 final CollisionAccess access = new CollisionAccess(sinfo);
67 this.river = this.access.getRiver(); 64 final River river = access.getRiver();
68 final RiverInfo riverInfo = new RiverInfo(this.river); 65 final RiverInfo riverInfo = new RiverInfo(river);
69 final DoubleRange calcRange = this.access.getRange(); 66
67 final DoubleRange calcRange = access.getRange();
70 68
71 // calculate results for each year or epoch 69 // calculate results for each year or epoch
72 this.problems = new Calculation(); 70 final Calculation problems = new Calculation();
73 this.qfinderProblemCount = 0; 71
74 final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); 72 final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());
75 73
76 final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, this.access.getYearsHeader()); 74 final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, access.getYearsHeader());
77 75
78 final Collection<ResultRow> overViewRows = new ArrayList<>(); 76 final Collection<ResultRow> overViewRows = new ArrayList<>();
79 77
80 this.years = new ArrayList<>(); 78 final List<DateRange> years = new ArrayList<>();
81 final NavigableSet<Integer> detailYears = new TreeSet<>(); 79 final NavigableSet<Integer> detailYears = new TreeSet<>();
82 if (this.access.getYears() != null) { 80 final List<CollisionCalcOverviewResult> singleYearResults = new ArrayList<>();
83 for (final int year : this.access.getYears()) { 81 if (access.getYears() != null) {
84 calculateOverview(overViewRows, year, year, false); 82 for (final int year : access.getYears()) {
85 this.years.add(new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))); 83 calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), year, year, false);
84 years.add(new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year)));
86 detailYears.add(Integer.valueOf(year)); 85 detailYears.add(Integer.valueOf(year));
87 } 86 }
88 } else { 87 } else {
89 for (final DateRange dr : this.access.getEpochs()) { 88 for (final DateRange dr : access.getEpochs()) {
90 calculateOverview(overViewRows, dr.getFromYear(), dr.getToYear(), true); 89 calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), dr.getFromYear(), dr.getToYear(), true);
91 this.years.add(dr); 90 years.add(dr);
92 detailYearsAdd(detailYears, dr); 91 detailYearsAdd(detailYears, dr);
93 } 92 }
94 } 93
95 final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(this.access.getYearsHeader(), (this.access.getYears() == null), 94 for (final Integer year : detailYears) {
96 this.years, overViewRows); 95
97 results.addResult(overviewResult, this.problems); 96 final Collection<ResultRow> yearRows = new ArrayList<>();
97 calculateOverview(yearRows, river, access.getLowerKm(), access.getUpperKm(), year, year, false);
98
99 if (!yearRows.isEmpty()) {
100 final DateRange yearRange = new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year));
101 final CollisionCalcOverviewResult yearResult = new CollisionCalcOverviewResult(Integer.toString(year), false,
102 Collections.singleton(yearRange), yearRows);
103 singleYearResults.add(yearResult);
104 }
105 }
106 }
107 final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(access.getYearsHeader(), (access.getYears() == null), years,
108 overViewRows);
109 results.addResult(overviewResult, problems);
110
111 /* add the single year from epochs, after the epochs, so they will be exported at the end of the table etc. */
112 for (final CollisionCalcOverviewResult result : singleYearResults)
113 results.addResult(result, problems);
98 114
99 // calculate secondary results for each year 115 // calculate secondary results for each year
100 final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders = new HashMap<>(); 116 final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders = new HashMap<>();
101 final Map<String, GaugeMainValueFinder> zoneFinders = new HashMap<>(); 117 final Map<String, GaugeMainValueFinder> zoneFinders = new HashMap<>();
102 final Collection<ResultRow> detailsRows = new ArrayList<>(); 118 final Collection<ResultRow> detailsRows = new ArrayList<>();
103 for (final Integer year : detailYears) 119 for (final Integer year : detailYears)
104 calculateDetails(detailsRows, year, qFinders, zoneFinders); 120 calculateDetails(detailsRows, river, access, year, qFinders, zoneFinders, problems, years, qfinderProblemCount);
105 final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows); 121 final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows);
106 results.addResult(detailResult, this.problems); 122 results.addResult(detailResult, problems);
107 123
108 return new CalculationResult(results, this.problems); 124 return new CalculationResult(results, problems);
109 } 125 }
110 126
111 /** 127 /**
112 * Adds all years of an epoch to a set 128 * Adds all years of an epoch to a set
113 */ 129 */
118 134
119 /** 135 /**
120 * Calculates the collision counts for a km range of a river and a year or year range (epoch), 136 * Calculates the collision counts for a km range of a river and a year or year range (epoch),
121 * and adds them to a ResultRow collection 137 * and adds them to a ResultRow collection
122 */ 138 */
123 private void calculateOverview(final Collection<ResultRow> rows, final int fromYear, final int toYear, final boolean isEpoch) { 139 private void calculateOverview(final Collection<ResultRow> rows, final River river, final double fromKm, final double toKm, final int fromYear,
124 for (final CollisionAggregateValue aggregate : CollisionAggregateValue.getValuesByKm(this.river, this.access.getLowerKm(), this.access.getUpperKm(), 140 final int toYear, final boolean isEpoch) {
125 fromYear, toYear)) { 141 for (final CollisionAggregateValue aggregate : CollisionAggregateValue.getValuesByKm(river, fromKm, toKm, fromYear, toYear)) {
126 rows.add(ResultRow.create().putValue(GeneralResultType.station, aggregate.getStation()) 142 rows.add(ResultRow.create().putValue(GeneralResultType.station, aggregate.getStation())
127 .putValue(SInfoResultType.years, yearsToString(isEpoch, fromYear, toYear)).putValue(SInfoResultType.collisionCount, aggregate.getCount())); 143 .putValue(SInfoResultType.years, yearsToString(isEpoch, fromYear, toYear)).putValue(SInfoResultType.collisionCount, aggregate.getCount()));
128 } 144 }
129 } 145 }
130 146
142 return (isEpoch ? String.format("%d-%d", fromYear, toYear) : Integer.toString(fromYear)); 158 return (isEpoch ? String.format("%d-%d", fromYear, toYear) : Integer.toString(fromYear));
143 } 159 }
144 160
145 /** 161 /**
146 * Calculates the collision details for a km range of a river and a year, and adds them to a ResultRow collection 162 * Calculates the collision details for a km range of a river and a year, and adds them to a ResultRow collection
147 */ 163 *
148 private void calculateDetails(final Collection<ResultRow> rows, final int year, final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, 164 * @param qfinderProblemCount
149 final Map<String, GaugeMainValueFinder> zoneFinders) { 165 */
150 166 private void calculateDetails(final Collection<ResultRow> rows, final River river, final CollisionAccess access, final int year,
151 for (final CollisionValue collision : CollisionValue.getValues(this.river, this.access.getLowerKm(), this.access.getUpperKm(), 167 final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final Map<String, GaugeMainValueFinder> zoneFinders,
152 DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))) { 168 final Calculation problems, final List<DateRange> years, final int qfinderProblemCount) {
169
170 final double fromKm = access.getLowerKm();
171 final double toKm = access.getUpperKm();
172
173 for (final CollisionValue collision : CollisionValue.getValues(river, fromKm, toKm, DateUtil.getStartDateFromYear(year),
174 DateUtil.getEndDateFromYear(year))) {
153 final String gaugeName = collision.getGaugeName(); 175 final String gaugeName = collision.getGaugeName();
154 final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), collision.getEventDate()); 176 final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), collision.getEventDate(), river, problems, years,
177 qfinderProblemCount);
155 final double qOut = Double.isInfinite(q) ? Double.NaN : q; 178 final double qOut = Double.isInfinite(q) ? Double.NaN : q;
156 final String zone = getZone(zoneFinders, gaugeName, q); 179 final String zone = getZone(zoneFinders, gaugeName, q, river, problems);
157 rows.add(ResultRow.create().putValue(GeneralResultType.station, collision.getStation()) 180 rows.add(ResultRow.create().putValue(GeneralResultType.station, collision.getStation())
158 .putValue(GeneralResultType.dateShort, collision.getEventDate()).putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW()) 181 .putValue(GeneralResultType.dateShort, collision.getEventDate()).putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW())
159 .putValue(GeneralResultType.gaugeLabel, gaugeName).putValue(SInfoResultType.dischargeLong, qOut) 182 .putValue(GeneralResultType.gaugeLabel, gaugeName).putValue(SInfoResultType.dischargeLong, qOut)
160 .putValue(SInfoResultType.dischargeZone, zone)); 183 .putValue(SInfoResultType.dischargeZone, zone));
161 } 184 }
162 } 185 }
163 186
164 /** 187 /**
165 * Gets the discharge of a gauge and a W 188 * Gets the discharge of a gauge and a W
166 */ 189 *
167 private double getQ(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName, final double w, final Date when) { 190 * @param years
191 * @param qfinderProblemCount
192 */
193 private double getQ(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName, final double w, final Date when,
194 final River river, final Calculation problems, final List<DateRange> years, int qfinderProblemCount) {
168 // Find the gauge and load its discharge table, if not already in the map 195 // Find the gauge and load its discharge table, if not already in the map
169 final String gnKey = gaugeName.toLowerCase(); 196 final String gnKey = gaugeName.toLowerCase();
170 if (!qFinders.containsKey(gnKey)) 197 if (!qFinders.containsKey(gnKey))
171 addQFinders(qFinders, gaugeName); 198 addQFinders(qFinders, gaugeName, problems, river, years);
199
200 // Interpolate W.
172 // Interpolate W. 201 // Interpolate W.
173 final GaugeDischargeValuesFinder qFinder = getQFinder(qFinders.get(gnKey), when); 202 final GaugeDischargeValuesFinder qFinder = getQFinder(qFinders.get(gnKey), when);
174 if (qFinder == null) { 203 if (qFinder == null) {
175 this.qfinderProblemCount++; 204 qfinderProblemCount++;
176 if (this.qfinderProblemCount == 1) 205 if (qfinderProblemCount == 1)
177 this.problems.addProblem("gauge_discharge_table.missing", gaugeName); 206 problems.addProblem("gauge_discharge_table.missing", gaugeName);
178 return Double.NaN; 207 return Double.NaN;
179 } 208 }
180 return qFinder.getDischarge(w); 209 return qFinder.getDischarge(w);
181 } 210 }
182 211
183 /** 212 /**
184 * Add the discharge finders for a gauge and the active time period 213 * Add the discharge finders for a gauge and the active time period
185 */ 214 */
186 private void addQFinders(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName) { 215 private void addQFinders(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName, final Calculation problems,
216 final River river, final List<DateRange> years) {
187 final String gnKey = gaugeName.toLowerCase(); 217 final String gnKey = gaugeName.toLowerCase();
188 final Gauge gauge = this.river.determineGaugeByName(gaugeName); 218 final Gauge gauge = river.determineGaugeByName(gaugeName);
189 if (gauge == null) { 219 if (gauge == null) {
190 qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>()); 220 qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>());
191 return; 221 return;
192 } 222 }
193 final List<DischargeTable> qtables = DischargeTable.fetchHistoricalDischargeTables(gauge, this.years.get(0).getFrom(), 223 final List<DischargeTable> qtables = DischargeTable.fetchHistoricalDischargeTables(gauge, years.get(0).getFrom(), years.get(years.size() - 1).getTo());
194 this.years.get(this.years.size() - 1).getTo());
195 qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>()); 224 qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>());
196 for (final DischargeTable qtable : qtables) 225 for (final DischargeTable qtable : qtables)
197 qFinders.get(gnKey).put(qtable.getTimeInterval().getStartTime(), 226 qFinders.get(gnKey).put(qtable.getTimeInterval().getStartTime(), GaugeDischargeValuesFinder.loadValues(qtable, river, gaugeName, problems));
198 GaugeDischargeValuesFinder.loadValues(qtable, this.river, gaugeName, this.problems));
199 } 227 }
200 228
201 /** 229 /**
202 * Searches a q values finder map for a date time 230 * Searches a q values finder map for a date time
203 */ 231 */
213 } 241 }
214 242
215 /** 243 /**
216 * Gets the main value zone name of a gauge and a Q 244 * Gets the main value zone name of a gauge and a Q
217 */ 245 */
218 private String getZone(final Map<String, GaugeMainValueFinder> zoneFinders, final String gaugeName, final double q) { 246 private String getZone(final Map<String, GaugeMainValueFinder> zoneFinders, final String gaugeName, final double q, final River river,
247 final Calculation problems) {
219 // Find the gauge and load its main value list, if not already in the map 248 // Find the gauge and load its main value list, if not already in the map
220 final String gnKey = gaugeName.toLowerCase(); 249 final String gnKey = gaugeName.toLowerCase();
221 if (!zoneFinders.containsKey(gnKey)) 250 if (!zoneFinders.containsKey(gnKey))
222 zoneFinders.put(gnKey, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, this.river, gaugeName, this.problems, "GLQ")); 251 zoneFinders.put(gnKey, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, river, gaugeName, problems, "GLQ"));
223 // Build the zone name 252 // Build the zone name
224 if (zoneFinders.get(gnKey) == null) 253 if (zoneFinders.get(gnKey) == null)
225 return ""; 254 return "";
226 return zoneFinders.get(gnKey).findZoneName(q); 255 return zoneFinders.get(gnKey).findZoneName(q);
227 } 256 }

http://dive4elements.wald.intevation.org