view artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelFetcher.java @ 9708:93389a05119e

zu Nachtrag - BezugsWST in intell. Datenkorb
author dnt_bjoernsen <d.tironi@bjoernsen.de>
date Fri, 22 Jan 2021 18:45:39 +0100
parents 3f49835a00c3
children
line wrap: on
line source
/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
 * Software engineering by
 *  Björnsen Beratende Ingenieure GmbH
 *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */
package org.dive4elements.river.artifacts.states;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.math.DoubleRange;
import org.apache.log4j.Logger;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.artifacts.AbstractFixBunduArtifact;
import org.dive4elements.river.artifacts.D4EArtifact;
import org.dive4elements.river.artifacts.StaticWKmsArtifact;
import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
import org.dive4elements.river.artifacts.WINFOArtifact;
import org.dive4elements.river.artifacts.access.FixRealizingAccess;
import org.dive4elements.river.artifacts.bundu.bezugswst.BezugswstCalculationResults;
import org.dive4elements.river.artifacts.bundu.bezugswst.BezugswstMainCalculationResult;
import org.dive4elements.river.artifacts.common.AbstractCalculationExportableResult;
import org.dive4elements.river.artifacts.model.Calculation;
import org.dive4elements.river.artifacts.model.CalculationResult;
import org.dive4elements.river.artifacts.model.Segment;
import org.dive4elements.river.artifacts.model.WKms;
import org.dive4elements.river.artifacts.model.WQKms;
import org.dive4elements.river.artifacts.model.WstColumnFactory;
import org.dive4elements.river.artifacts.model.fixings.FixRealizingCalculationExtended;
import org.dive4elements.river.artifacts.model.fixings.FixRealizingResult;
import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
import org.dive4elements.river.model.TimeInterval;
import org.dive4elements.river.model.WstColumn;
import org.dive4elements.river.utils.RiverUtils;

/**
 * Helper class that accesses existing waterlevels (in most cases) from the
 * data-cage. This is mainly a refaktoring of
 * org.dive4elements.river.artifacts.states.WDifferencesState.getWKms(String,
 * CallContext, double, double), adding more infos to the fetched water levels.
 *
 * @author Gernot Belger
 */
public class WaterlevelFetcher {
	private static Logger log = Logger.getLogger(WaterlevelFetcher.class);

	/**
	 * @param simulationRange
	 *            This range is used to check if the found waterlevel covers it. It
	 *            is NOT used to reduce the fetched data, because in case of
	 *            waterlevels we always need to full set in order to determine the
	 *            relevant gauge.
	 */
	public WaterlevelData findWaterlevel(final CallContext context, final String mingle,
			final DoubleRange simulationRange, final Calculation problems) {

		final String[] def = mingle.split(";");
		final String uuid = def[0];
		// final String factory = def[1];
		final int idx = Integer.parseInt(def[2]);
		final String name = def[3];
		final D4EArtifact d4eArtifact = RiverUtils.getArtifact(uuid, context);
		if (d4eArtifact == null)
			return null;

		final WaterlevelData data = fetchWaterlevelFromArtifact(context, d4eArtifact, idx, Double.NaN, Double.NaN);
		if (data == null) {
			problems.addProblem("waterlevelfetcher.missing", mingle);
			return null;
		}

		if (!data.covers(simulationRange)) {
			problems.addProblem("waterlevelfetcher.empty", data.getName());
			return null;
		}

		return data.withName(name);
	}

	private WaterlevelData fetchWaterlevelFromArtifact(final CallContext context, final D4EArtifact d4eArtifact,
			final int idx, final double from, final double to) {

		if (d4eArtifact instanceof StaticWKmsArtifact)
			return fetchStaticWKmsArtifactWaterlevel((StaticWKmsArtifact) d4eArtifact, from, to);

		if (d4eArtifact instanceof StaticWQKmsArtifact)
			return fetchStaticWQKmsArtifactWaterlevel((StaticWQKmsArtifact) d4eArtifact, from, to);

		if (d4eArtifact instanceof WINFOArtifact)
			return fetchWINFOArtifactWaterlevel(context, (WINFOArtifact) d4eArtifact, idx, from, to);

		if (d4eArtifact instanceof AbstractFixBunduArtifact) //including BUNDUArtifact
			return fetchFixationArtifactWaterlevel(context, (AbstractFixBunduArtifact) d4eArtifact, idx, from, to);

		log.warn(String.format("Get Waterlevel from %s not implemented!", d4eArtifact.getClass().getSimpleName()));
		return null;
	}

	// REMARK: instead of several helper methods here this would be a good place for
	// abstraction, in order to push
	// this logic back to the corresponding artifacts. However this will most
	// certainly break existing
	// artifact-serialization

	private WaterlevelData fetchStaticWKmsArtifactWaterlevel(final StaticWKmsArtifact staticWKms, final double from,
			final double to) {

		log.debug("WDifferencesState obtain data from StaticWKms");

		final WKms wkms = staticWKms.getWKms(from, to);

		if (wkms != null) {
			final int year = fetchStaticWKmsYear(staticWKms);
			return new WaterlevelData(wkms, year, false, false);
		}

		log.error("No WKms from Static artifact for this range.");
		return null;
	}

	private WaterlevelData fetchStaticWQKmsArtifactWaterlevel(final StaticWQKmsArtifact staticWKms, final double from,
			final double to) {

		log.debug("WDifferencesState obtain data from StaticWQKms");

		final WQKms wkms = staticWKms.getWQKms(from, to);

		if (wkms != null) {
			final int year = fetchStaticWKmsYear(staticWKms);
			return new WaterlevelData(wkms, year, false, false);
		}

		log.error("No WKms from Static artifact for this range.");
		return null;
	}

	private WaterlevelData fetchWINFOArtifactWaterlevel(final CallContext context, final WINFOArtifact flys,
			final int idx, final double from, final double to) {
		log.debug("Get WKms from WINFOArtifact");

		final WKms[] wkms = (WKms[]) flys.getWaterlevelData(context).getData();

		if (wkms == null || wkms.length == 0) {
			log.warn("no waterlevels in artifact");
			return null;
		}

		if (wkms.length < idx + 1) {
			log.warn("Not enough waterlevels in artifact.");
			return null;
		}

		// REAMRK: W_INFO results does not know any 'year'
		final int year = -1;
		return new WaterlevelData(wkms[idx], year, false, true).filterByRange(from, to);
	}

	// private WaterlevelData fetchBUNDUArtifactWaterlevel(final CallContext
	// context, final BUNDUArtifact flys,
	// final int idx, final double from, final double to) {
	// log.debug("Get WKms from BUNDUArtifact");
	//
	// final WKms[] wkms = (WKms[]) flys.getWaterlevelData(context).getData();
	//
	// if (wkms == null || wkms.length == 0) {
	// log.warn("no waterlevels in artifact");
	// return null;
	// }
	//
	// if (wkms.length < idx + 1) {
	// log.warn("Not enough waterlevels in artifact.");
	// return null;
	// }
	//
	// // REAMRK: W_INFO results does not know any 'year'
	// final int year = -1;
	// return new WaterlevelData(wkms[idx], year, false, true).filterByRange(from,
	// to);
	// }

	private WaterlevelData fetchFixationArtifactWaterlevel(final CallContext context,
			final AbstractFixBunduArtifact artifact, final int idx, final double from, final double to) {

		log.debug("Get WKms from FixationArtifact or BUNDUArtifact.bezugswst");

		final Object r = artifact.compute(context, ComputeType.ADVANCE, false);
		if (r instanceof CalculationResult) {
			final Object frR = ((CalculationResult) r).getData();
			
			///For BezugsWST
			if (frR instanceof BezugswstCalculationResults) {
				List<AbstractCalculationExportableResult> results = ((BezugswstCalculationResults)frR).getResults();
				BezugswstMainCalculationResult mainResult = this.getBezugsWstMainResultFromList(results);
				if (mainResult!=null) {
					//Taken FROM BezugswstCalculation
				     // We have no wst year as the wst is created by a calculation; we do not need it though
			        final int wspYear = -1;
			        // Remark: showAllGauges true for Fixierungsanalyse, false for WInfo, so true here as well
			        final boolean showAllGauges = true;
					return new WaterlevelData( mainResult.getWQKms() , wspYear, showAllGauges, true)
							.filterByRange(from, to);
				}
			}
			
			else if (frR instanceof FixRealizingResult) {
				// Get W/Q input per gauge for this case.
				final FixRealizingAccess fixAccess = new FixRealizingAccess(artifact);
				final List<Segment> segments = fixAccess.getSegments();
				final boolean isFixRealize = (segments != null && !segments.isEmpty());

				/*
				 * ugly but necessary to keep this logic at least a bit inside the FixRealizing
				 * stuff
				 */
				final FixRealizingCalculationExtended calculation = new FixRealizingCalculationExtended(fixAccess);
				final int year = calculation.determineMeanYear();

				// REMARK: same logic as in WaterlevelExporter
				final boolean showAllGauges = isFixRealize;

				return new WaterlevelData(((FixRealizingResult) frR).getWQKms()[idx], year, showAllGauges, true)
						.filterByRange(from, to);
			}
		}
		return null;
	}
	
	private BezugswstMainCalculationResult getBezugsWstMainResultFromList(List<AbstractCalculationExportableResult> results) {
		for (	Object result : results)
			if (result instanceof BezugswstMainCalculationResult)
				return (BezugswstMainCalculationResult) result;
		return null;
	}
 
	/**
	 * Fetches the 'year' for a staticXXX-artifact. REMARK: actually this should
	 * happen inside the staticWKms artifact and eventually in the WKmsFactory, but
	 * the code there is already awful and it will also break the old
	 * artifact-serialization...
	 */
	private int fetchStaticWKmsYear(final D4EArtifact staticWKms) {

		final int colPos = Integer.parseInt(staticWKms.getDataAsString("col_pos"));
		final int wstId = Integer.parseInt(staticWKms.getDataAsString("wst_id"));

		final WstColumn wstColumn = WstColumnFactory.getWstColumn(wstId, colPos);
		final TimeInterval timeInterval = wstColumn.getTimeInterval();
		if (timeInterval == null)
			return -1;

		final Date startTime = timeInterval.getStartTime();
		if (startTime == null)
			return -1;

		// REMARK: the times are stored without timezone in the DB, so it is unclear
		// what hibernate makes of it.
		// We simply use the default timezone here and hope we never get problems...
		// 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
		// no change anything regarding the year.
		final Calendar cal = Calendar.getInstance();
		cal.setTime(startTime);
		return cal.get(Calendar.YEAR);
	}
}

http://dive4elements.wald.intevation.org