gernotbelger@8881: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde gernotbelger@8881: * Software engineering by gernotbelger@8881: * Björnsen Beratende Ingenieure GmbH gernotbelger@8881: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt gernotbelger@8881: * gernotbelger@8881: * This file is Free Software under the GNU AGPL (>=v3) gernotbelger@8881: * and comes with ABSOLUTELY NO WARRANTY! Check out the gernotbelger@8881: * documentation coming with Dive4Elements River for details. gernotbelger@8881: */ gernotbelger@8881: package org.dive4elements.river.artifacts.states; gernotbelger@8881: gernotbelger@8962: import java.util.Calendar; gernotbelger@8962: import java.util.Date; gernotbelger@8881: import java.util.List; gernotbelger@8881: gernotbelger@8964: import org.apache.commons.lang.math.DoubleRange; gernotbelger@8881: import org.apache.log4j.Logger; gernotbelger@8881: import org.dive4elements.artifacts.CallContext; d@9708: import org.dive4elements.river.artifacts.AbstractFixBunduArtifact; gernotbelger@8881: import org.dive4elements.river.artifacts.D4EArtifact; gernotbelger@8881: import org.dive4elements.river.artifacts.StaticWKmsArtifact; gernotbelger@8881: import org.dive4elements.river.artifacts.StaticWQKmsArtifact; gernotbelger@8881: import org.dive4elements.river.artifacts.WINFOArtifact; gernotbelger@8881: import org.dive4elements.river.artifacts.access.FixRealizingAccess; d@9708: import org.dive4elements.river.artifacts.bundu.bezugswst.BezugswstCalculationResults; d@9708: import org.dive4elements.river.artifacts.bundu.bezugswst.BezugswstMainCalculationResult; d@9708: import org.dive4elements.river.artifacts.common.AbstractCalculationExportableResult; gernotbelger@8946: import org.dive4elements.river.artifacts.model.Calculation; gernotbelger@8881: import org.dive4elements.river.artifacts.model.CalculationResult; gernotbelger@8881: import org.dive4elements.river.artifacts.model.Segment; gernotbelger@8881: import org.dive4elements.river.artifacts.model.WKms; gernotbelger@8881: import org.dive4elements.river.artifacts.model.WQKms; gernotbelger@8962: import org.dive4elements.river.artifacts.model.WstColumnFactory; gernotbelger@8883: import org.dive4elements.river.artifacts.model.fixings.FixRealizingCalculationExtended; gernotbelger@8881: import org.dive4elements.river.artifacts.model.fixings.FixRealizingResult; gernotbelger@8881: import org.dive4elements.river.artifacts.states.DefaultState.ComputeType; gernotbelger@8962: import org.dive4elements.river.model.TimeInterval; gernotbelger@8962: import org.dive4elements.river.model.WstColumn; gernotbelger@8881: import org.dive4elements.river.utils.RiverUtils; gernotbelger@8881: gernotbelger@8881: /** d@9708: * Helper class that accesses existing waterlevels (in most cases) from the d@9708: * data-cage. This is mainly a refaktoring of d@9708: * org.dive4elements.river.artifacts.states.WDifferencesState.getWKms(String, gernotbelger@8881: * CallContext, double, double), adding more infos to the fetched water levels. gernotbelger@8881: * gernotbelger@8881: * @author Gernot Belger gernotbelger@8881: */ gernotbelger@8881: public class WaterlevelFetcher { d@9708: private static Logger log = Logger.getLogger(WaterlevelFetcher.class); gernotbelger@8881: d@9708: /** d@9708: * @param simulationRange d@9708: * This range is used to check if the found waterlevel covers it. It d@9708: * is NOT used to reduce the fetched data, because in case of d@9708: * waterlevels we always need to full set in order to determine the d@9708: * relevant gauge. d@9708: */ d@9708: public WaterlevelData findWaterlevel(final CallContext context, final String mingle, d@9708: final DoubleRange simulationRange, final Calculation problems) { gernotbelger@8881: d@9708: final String[] def = mingle.split(";"); d@9708: final String uuid = def[0]; d@9708: // final String factory = def[1]; d@9708: final int idx = Integer.parseInt(def[2]); d@9708: final String name = def[3]; d@9708: final D4EArtifact d4eArtifact = RiverUtils.getArtifact(uuid, context); d@9708: if (d4eArtifact == null) d@9708: return null; gernotbelger@8962: d@9708: final WaterlevelData data = fetchWaterlevelFromArtifact(context, d4eArtifact, idx, Double.NaN, Double.NaN); d@9708: if (data == null) { d@9708: problems.addProblem("waterlevelfetcher.missing", mingle); d@9708: return null; d@9708: } gernotbelger@8962: d@9708: if (!data.covers(simulationRange)) { d@9708: problems.addProblem("waterlevelfetcher.empty", data.getName()); d@9708: return null; d@9708: } gernotbelger@8962: d@9708: return data.withName(name); d@9708: } d@9708: d@9708: private WaterlevelData fetchWaterlevelFromArtifact(final CallContext context, final D4EArtifact d4eArtifact, d@9708: final int idx, final double from, final double to) { d@9708: d@9708: if (d4eArtifact instanceof StaticWKmsArtifact) d@9708: return fetchStaticWKmsArtifactWaterlevel((StaticWKmsArtifact) d4eArtifact, from, to); d@9708: d@9708: if (d4eArtifact instanceof StaticWQKmsArtifact) d@9708: return fetchStaticWQKmsArtifactWaterlevel((StaticWQKmsArtifact) d4eArtifact, from, to); d@9708: d@9708: if (d4eArtifact instanceof WINFOArtifact) d@9708: return fetchWINFOArtifactWaterlevel(context, (WINFOArtifact) d4eArtifact, idx, from, to); d@9708: d@9708: if (d4eArtifact instanceof AbstractFixBunduArtifact) //including BUNDUArtifact d@9708: return fetchFixationArtifactWaterlevel(context, (AbstractFixBunduArtifact) d4eArtifact, idx, from, to); d@9708: d@9708: log.warn(String.format("Get Waterlevel from %s not implemented!", d4eArtifact.getClass().getSimpleName())); d@9708: return null; d@9708: } d@9708: d@9708: // REMARK: instead of several helper methods here this would be a good place for d@9708: // abstraction, in order to push d@9708: // this logic back to the corresponding artifacts. However this will most d@9708: // certainly break existing d@9708: // artifact-serialization d@9708: d@9708: private WaterlevelData fetchStaticWKmsArtifactWaterlevel(final StaticWKmsArtifact staticWKms, final double from, d@9708: final double to) { d@9708: d@9708: log.debug("WDifferencesState obtain data from StaticWKms"); d@9708: d@9708: final WKms wkms = staticWKms.getWKms(from, to); d@9708: d@9708: if (wkms != null) { d@9708: final int year = fetchStaticWKmsYear(staticWKms); d@9708: return new WaterlevelData(wkms, year, false, false); d@9708: } d@9708: d@9708: log.error("No WKms from Static artifact for this range."); d@9708: return null; d@9708: } d@9708: d@9708: private WaterlevelData fetchStaticWQKmsArtifactWaterlevel(final StaticWQKmsArtifact staticWKms, final double from, d@9708: final double to) { d@9708: d@9708: log.debug("WDifferencesState obtain data from StaticWQKms"); d@9708: d@9708: final WQKms wkms = staticWKms.getWQKms(from, to); d@9708: d@9708: if (wkms != null) { d@9708: final int year = fetchStaticWKmsYear(staticWKms); d@9708: return new WaterlevelData(wkms, year, false, false); d@9708: } d@9708: d@9708: log.error("No WKms from Static artifact for this range."); d@9708: return null; d@9708: } d@9708: d@9708: private WaterlevelData fetchWINFOArtifactWaterlevel(final CallContext context, final WINFOArtifact flys, d@9708: final int idx, final double from, final double to) { d@9708: log.debug("Get WKms from WINFOArtifact"); d@9708: d@9708: final WKms[] wkms = (WKms[]) flys.getWaterlevelData(context).getData(); d@9708: d@9708: if (wkms == null || wkms.length == 0) { d@9708: log.warn("no waterlevels in artifact"); d@9708: return null; d@9708: } d@9708: d@9708: if (wkms.length < idx + 1) { d@9708: log.warn("Not enough waterlevels in artifact."); d@9708: return null; d@9708: } d@9708: d@9708: // REAMRK: W_INFO results does not know any 'year' d@9708: final int year = -1; d@9708: return new WaterlevelData(wkms[idx], year, false, true).filterByRange(from, to); d@9708: } d@9708: d@9708: // private WaterlevelData fetchBUNDUArtifactWaterlevel(final CallContext d@9708: // context, final BUNDUArtifact flys, d@9708: // final int idx, final double from, final double to) { d@9708: // log.debug("Get WKms from BUNDUArtifact"); d@9708: // d@9708: // final WKms[] wkms = (WKms[]) flys.getWaterlevelData(context).getData(); d@9708: // d@9708: // if (wkms == null || wkms.length == 0) { d@9708: // log.warn("no waterlevels in artifact"); d@9708: // return null; d@9708: // } d@9708: // d@9708: // if (wkms.length < idx + 1) { d@9708: // log.warn("Not enough waterlevels in artifact."); d@9708: // return null; d@9708: // } d@9708: // d@9708: // // REAMRK: W_INFO results does not know any 'year' d@9708: // final int year = -1; d@9708: // return new WaterlevelData(wkms[idx], year, false, true).filterByRange(from, d@9708: // to); d@9708: // } d@9708: d@9708: private WaterlevelData fetchFixationArtifactWaterlevel(final CallContext context, d@9708: final AbstractFixBunduArtifact artifact, final int idx, final double from, final double to) { d@9708: d@9708: log.debug("Get WKms from FixationArtifact or BUNDUArtifact.bezugswst"); d@9708: d@9708: final Object r = artifact.compute(context, ComputeType.ADVANCE, false); d@9708: if (r instanceof CalculationResult) { d@9708: final Object frR = ((CalculationResult) r).getData(); d@9708: d@9708: ///For BezugsWST d@9708: if (frR instanceof BezugswstCalculationResults) { d@9708: List results = ((BezugswstCalculationResults)frR).getResults(); d@9708: BezugswstMainCalculationResult mainResult = this.getBezugsWstMainResultFromList(results); d@9708: if (mainResult!=null) { d@9708: //Taken FROM BezugswstCalculation d@9708: // We have no wst year as the wst is created by a calculation; we do not need it though d@9708: final int wspYear = -1; d@9708: // Remark: showAllGauges true for Fixierungsanalyse, false for WInfo, so true here as well d@9708: final boolean showAllGauges = true; d@9708: return new WaterlevelData( mainResult.getWQKms() , wspYear, showAllGauges, true) d@9708: .filterByRange(from, to); d@9708: } d@9708: } d@9708: d@9708: else if (frR instanceof FixRealizingResult) { d@9708: // Get W/Q input per gauge for this case. d@9708: final FixRealizingAccess fixAccess = new FixRealizingAccess(artifact); d@9708: final List segments = fixAccess.getSegments(); d@9708: final boolean isFixRealize = (segments != null && !segments.isEmpty()); d@9708: d@9708: /* d@9708: * ugly but necessary to keep this logic at least a bit inside the FixRealizing d@9708: * stuff d@9708: */ d@9708: final FixRealizingCalculationExtended calculation = new FixRealizingCalculationExtended(fixAccess); d@9708: final int year = calculation.determineMeanYear(); d@9708: d@9708: // REMARK: same logic as in WaterlevelExporter d@9708: final boolean showAllGauges = isFixRealize; d@9708: d@9708: return new WaterlevelData(((FixRealizingResult) frR).getWQKms()[idx], year, showAllGauges, true) d@9708: .filterByRange(from, to); d@9708: } d@9708: } d@9708: return null; d@9708: } d@9708: d@9708: private BezugswstMainCalculationResult getBezugsWstMainResultFromList(List results) { d@9708: for ( Object result : results) d@9708: if (result instanceof BezugswstMainCalculationResult) d@9708: return (BezugswstMainCalculationResult) result; d@9708: return null; d@9708: } d@9708: d@9708: /** d@9708: * Fetches the 'year' for a staticXXX-artifact. REMARK: actually this should d@9708: * happen inside the staticWKms artifact and eventually in the WKmsFactory, but d@9708: * the code there is already awful and it will also break the old d@9708: * artifact-serialization... d@9708: */ d@9708: private int fetchStaticWKmsYear(final D4EArtifact staticWKms) { d@9708: d@9708: final int colPos = Integer.parseInt(staticWKms.getDataAsString("col_pos")); d@9708: final int wstId = Integer.parseInt(staticWKms.getDataAsString("wst_id")); d@9708: d@9708: final WstColumn wstColumn = WstColumnFactory.getWstColumn(wstId, colPos); d@9708: final TimeInterval timeInterval = wstColumn.getTimeInterval(); d@9708: if (timeInterval == null) d@9708: return -1; d@9708: d@9708: final Date startTime = timeInterval.getStartTime(); d@9708: if (startTime == null) d@9708: return -1; d@9708: d@9708: // REMARK: the times are stored without timezone in the DB, so it is unclear d@9708: // what hibernate makes of it. d@9708: // We simply use the default timezone here and hope we never get problems... d@9708: // Actually we always have 12:00 as time in the db data, so a smal timeshift due d@9708: // to winter/sommertime or UTC/GMT+1 will d@9708: // no change anything regarding the year. d@9708: final Calendar cal = Calendar.getInstance(); d@9708: cal.setTime(startTime); d@9708: return cal.get(Calendar.YEAR); d@9708: } gernotbelger@8883: }