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@8881: import org.apache.log4j.Logger; gernotbelger@8881: import org.dive4elements.artifacts.CallContext; gernotbelger@8881: import org.dive4elements.river.artifacts.D4EArtifact; gernotbelger@8881: import org.dive4elements.river.artifacts.FixationArtifact; 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; 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: /** gernotbelger@8881: * Helper class that accesses existing waterlevels (in most cases) from the data-cage. gernotbelger@8881: * This is mainly a refaktoring of 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 { gernotbelger@8881: private static Logger log = Logger.getLogger(WaterlevelFetcher.class); gernotbelger@8881: gernotbelger@8881: public WaterlevelData findWaterlevel(final CallContext context, final String mingle, final double from, gernotbelger@8946: final double to, final Calculation problems) { gernotbelger@8881: gernotbelger@8881: final String[] def = mingle.split(";"); gernotbelger@8881: final String uuid = def[0]; gernotbelger@8947: // final String factory = def[1]; gernotbelger@8881: final int idx = Integer.parseInt(def[2]); gernotbelger@8947: final String name = def[3]; gernotbelger@8881: final D4EArtifact d4eArtifact = RiverUtils.getArtifact(uuid, context); gernotbelger@8881: gernotbelger@8881: final WaterlevelData data = fetchWaterlevelFromArtifact(context, d4eArtifact, idx, from, to); gernotbelger@8946: if (data != null) gernotbelger@8946: return data.withName(name); gernotbelger@8946: gernotbelger@8946: problems.addProblem("waterlevelfetcher.missing'", mingle); gernotbelger@8946: return null; gernotbelger@8881: } gernotbelger@8881: gernotbelger@8881: private WaterlevelData fetchWaterlevelFromArtifact(final CallContext context, final D4EArtifact d4eArtifact, gernotbelger@8881: final int idx, final double from, final double to) { gernotbelger@8881: if (d4eArtifact == null) { gernotbelger@8881: log.warn("One of the artifacts (1) for diff calculation " + "could not be loaded"); gernotbelger@8881: return null; gernotbelger@8881: } gernotbelger@8881: gernotbelger@8881: if (d4eArtifact instanceof StaticWKmsArtifact) { gernotbelger@8881: return fetchStaticWKmsArtifactWaterlevel((StaticWKmsArtifact) d4eArtifact, idx, from, to); gernotbelger@8881: } gernotbelger@8881: gernotbelger@8881: if (d4eArtifact instanceof StaticWQKmsArtifact) { gernotbelger@8881: return fetchStaticWQKmsArtifactWaterlevel((StaticWQKmsArtifact) d4eArtifact, from, to); gernotbelger@8881: } gernotbelger@8881: gernotbelger@8881: if (d4eArtifact instanceof WINFOArtifact) gernotbelger@8881: return fetchWINFOArtifactWaterlevel(context, (WINFOArtifact) d4eArtifact, idx, from, to); gernotbelger@8881: gernotbelger@8881: if (d4eArtifact instanceof FixationArtifact) gernotbelger@8881: return fetchFixationArtifactWaterlevel(context, (FixationArtifact) d4eArtifact, idx, from, to); gernotbelger@8881: gernotbelger@8881: log.warn(String.format("Get Waterlevel from %s not implemented! )", d4eArtifact.getClass().getSimpleName())); gernotbelger@8881: return null; gernotbelger@8881: } gernotbelger@8881: gernotbelger@8962: // REMARK: instead of several helper methods here this would be a good place for abstraction, in order to push gernotbelger@8962: // this logic back to the corresponding artifacts. However this will most certainly break existing gernotbelger@8962: // artifact-serialization gernotbelger@8881: gernotbelger@8881: private WaterlevelData fetchStaticWKmsArtifactWaterlevel(final StaticWKmsArtifact staticWKms, final int idx, gernotbelger@8881: final double from, final double to) { gernotbelger@8881: gernotbelger@8881: log.debug("WDifferencesState obtain data from StaticWKms"); gernotbelger@8881: gernotbelger@8881: final WKms wkms = staticWKms.getWKms(idx, from, to); gernotbelger@8881: gernotbelger@8881: if (wkms != null) gernotbelger@8883: { gernotbelger@8962: final int year = fetchStaticWKmsYear(staticWKms); gernotbelger@8883: return new WaterlevelData(wkms, year); gernotbelger@8883: } gernotbelger@8881: gernotbelger@8881: log.error("No WKms from Static artifact for this range."); gernotbelger@8881: return null; gernotbelger@8881: } gernotbelger@8881: gernotbelger@8881: private WaterlevelData fetchStaticWQKmsArtifactWaterlevel(final StaticWQKmsArtifact staticWKms, final double from, gernotbelger@8881: final double to) { gernotbelger@8881: gernotbelger@8881: log.debug("WDifferencesState obtain data from StaticWQKms"); gernotbelger@8881: gernotbelger@8881: final WQKms wkms = staticWKms.getWQKms(from, to); gernotbelger@8881: gernotbelger@8881: if (wkms != null) gernotbelger@8883: { gernotbelger@8962: final int year = fetchStaticWKmsYear(staticWKms); gernotbelger@8883: return new WaterlevelData(wkms, year); gernotbelger@8883: } gernotbelger@8881: gernotbelger@8881: log.error("No WKms from Static artifact for this range."); gernotbelger@8881: return null; gernotbelger@8881: } gernotbelger@8881: gernotbelger@8881: private WaterlevelData fetchWINFOArtifactWaterlevel(final CallContext context, final WINFOArtifact flys, gernotbelger@8881: final int idx, final double from, final double to) { gernotbelger@8881: log.debug("Get WKms from WINFOArtifact"); gernotbelger@8881: gernotbelger@8881: final WKms[] wkms = (WKms[]) flys.getWaterlevelData(context).getData(); gernotbelger@8881: gernotbelger@8881: if (wkms == null || wkms.length == 0) { gernotbelger@8881: log.warn("no waterlevels in artifact"); gernotbelger@8881: return null; gernotbelger@8881: } gernotbelger@8881: gernotbelger@8881: if (wkms.length < idx + 1) { gernotbelger@8881: log.warn("Not enough waterlevels in artifact."); gernotbelger@8881: return null; gernotbelger@8881: } gernotbelger@8881: gernotbelger@8883: // REAMRK: W_INFO results does not know any 'year' gernotbelger@8883: final int year = -1; gernotbelger@8883: return new WaterlevelData(wkms[idx], year).filterByRange(from, to); gernotbelger@8881: } gernotbelger@8881: gernotbelger@8881: private WaterlevelData fetchFixationArtifactWaterlevel(final CallContext context, gernotbelger@8881: final FixationArtifact fixation, final int idx, final double from, final double to) { gernotbelger@8881: gernotbelger@8881: log.debug("Get WKms from FixationArtifact."); gernotbelger@8881: gernotbelger@8881: final CalculationResult r = (CalculationResult) fixation.compute(context, ComputeType.ADVANCE, false); gernotbelger@8881: final FixRealizingResult frR = (FixRealizingResult) r.getData(); gernotbelger@8881: gernotbelger@8881: // Get W/Q input per gauge for this case. gernotbelger@8881: final FixRealizingAccess fixAccess = new FixRealizingAccess(fixation); gernotbelger@8881: final List segments = fixAccess.getSegments(); gernotbelger@8881: final boolean isFixRealize = (segments != null && !segments.isEmpty()); gernotbelger@8881: gernotbelger@8883: /* ugly but necessary to keep this logic at least a bit inside the FixRealizing stuff */ gernotbelger@8883: final FixRealizingCalculationExtended calculation = new FixRealizingCalculationExtended(fixAccess); gernotbelger@8883: final int year = calculation.determineMeanYear(); gernotbelger@8883: gernotbelger@8881: // REMARK: same logic as in WaterlevelExporter gernotbelger@8881: final boolean showAllGauges = isFixRealize; gernotbelger@8881: gernotbelger@8883: return new WaterlevelData(frR.getWQKms()[idx], year, showAllGauges).filterByRange(from, to); gernotbelger@8881: } gernotbelger@8962: gernotbelger@8962: /** gernotbelger@8962: * Fetches the 'year' for a staticXXX-artifact. gernotbelger@8962: * REMARK: actually this should happen inside the staticWKms artifact and eventually in the WKmsFactory, but the code gernotbelger@8962: * there is already awful and it will also break the old artifact-serialization... gernotbelger@8962: */ gernotbelger@8962: private int fetchStaticWKmsYear(final D4EArtifact staticWKms) { gernotbelger@8962: gernotbelger@8962: final int colPos = Integer.parseInt(staticWKms.getDataAsString("col_pos")); gernotbelger@8962: final int wstId = Integer.parseInt(staticWKms.getDataAsString("wst_id")); gernotbelger@8962: gernotbelger@8962: final WstColumn wstColumn = WstColumnFactory.getWstColumn(wstId, colPos); gernotbelger@8962: final TimeInterval timeInterval = wstColumn.getTimeInterval(); gernotbelger@8962: if (timeInterval == null) gernotbelger@8962: return -1; gernotbelger@8962: gernotbelger@8962: final Date startTime = timeInterval.getStartTime(); gernotbelger@8962: if (startTime == null) gernotbelger@8962: return -1; gernotbelger@8962: gernotbelger@8962: // REMARK: the times are stored without timezone in the DB, so it is unclear what hibernate makes of it. gernotbelger@8962: // We simply use the default timezone here and hope we never get problems... gernotbelger@8962: // Actually we always have 12:00 as time in the db data, so a smal timeshift due to winter/sommertime or UTC/GMT+1 will gernotbelger@8962: // no change anything regarding the year. gernotbelger@8962: final Calendar cal = Calendar.getInstance(); gernotbelger@8962: cal.setTime(startTime); gernotbelger@8962: return cal.get(Calendar.YEAR); gernotbelger@8962: } gernotbelger@8883: }