gernotbelger@8933: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde gernotbelger@8933: * Software engineering by gernotbelger@8933: * Björnsen Beratende Ingenieure GmbH gernotbelger@8933: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt gernotbelger@8933: * gernotbelger@8933: * This file is Free Software under the GNU AGPL (>=v3) gernotbelger@8933: * and comes with ABSOLUTELY NO WARRANTY! Check out the gernotbelger@8933: * documentation coming with Dive4Elements River for details. gernotbelger@8933: */ gernotbelger@8933: package org.dive4elements.river.exports; gernotbelger@8933: gernotbelger@8933: import java.text.NumberFormat; gernotbelger@8933: gernotbelger@8933: import org.dive4elements.artifacts.CallContext; gernotbelger@8933: import org.dive4elements.river.artifacts.D4EArtifact; gernotbelger@8933: import org.dive4elements.river.artifacts.WINFOArtifact; gernotbelger@8933: import org.dive4elements.river.artifacts.model.WQKms; gernotbelger@8933: import org.dive4elements.river.artifacts.resources.Resources; gernotbelger@8933: import org.dive4elements.river.utils.Formatter; gernotbelger@8933: import org.dive4elements.river.utils.RiverUtils; gernotbelger@8933: import org.dive4elements.river.utils.RiverUtils.WQ_MODE; gernotbelger@8933: gernotbelger@8933: /** gernotbelger@8933: * Helper that encapsulates the logic how the 'description' column in waterlevel exporting is generated. gernotbelger@8933: * TODO: this class should also be used in WaterlevelExport (all code is copied from there), but this would involve gernotbelger@8933: * heavy testing and we leave this to the one who is responsible to clean up this mess. gernotbelger@8933: * gernotbelger@8933: * @author Gernot Belger gernotbelger@8933: */ gernotbelger@8933: public final class WaterlevelDescriptionBuilder { gernotbelger@8933: gernotbelger@9040: private static final String CSV_META_Q = "export.waterlevel.csv.meta.q"; gernotbelger@9040: gernotbelger@9040: // FIXME: unit 'NN + m' is wrong: gernotbelger@9040: // - use river-elevation system in case of absolute heights gernotbelger@9040: // - use 'cm am Pegel' in other cases gernotbelger@9040: private static final String CSV_META_W = "export.waterlevel.csv.meta.w"; gernotbelger@9040: gernotbelger@8933: private static final String CSV_Q_DESC_HEADER = "export.waterlevel.csv.header.q.desc"; gernotbelger@8933: gernotbelger@8933: // FIXME: missing in resource-files! hence always the default is used... gernotbelger@8933: private static final String CSV_W_DESC_HEADER = "export.waterlevel.csv.header.w.desc"; gernotbelger@8933: gernotbelger@8933: private static final String DEFAULT_CSV_Q_DESC_HEADER = "Bezeichnung"; gernotbelger@8933: gernotbelger@8933: private static final String DEFAULT_CSV_W_DESC_HEADER = "W/Pegel [cm]"; gernotbelger@8933: gernotbelger@8933: private final boolean isQ; gernotbelger@8933: gernotbelger@8933: private final boolean atGauge; gernotbelger@8933: gernotbelger@8933: private final CallContext context; gernotbelger@8933: gernotbelger@8933: private final WQ_MODE mode; gernotbelger@8933: gernotbelger@8933: private final D4EArtifact artifact; gernotbelger@8933: gernotbelger@8933: public WaterlevelDescriptionBuilder(final D4EArtifact artifact, final CallContext context) { gernotbelger@8933: this.context = context; gernotbelger@8933: gernotbelger@8933: // REMARK: taken from WaterlevelExporter, should be moved into WInfoArtifact gernotbelger@8933: this.mode = RiverUtils.getWQMode(artifact); gernotbelger@8933: this.atGauge = this.mode == WQ_MODE.QGAUGE || this.mode == WQ_MODE.WGAUGE; gernotbelger@8933: this.isQ = this.mode == WQ_MODE.QGAUGE || this.mode == WQ_MODE.QFREE; gernotbelger@8933: gernotbelger@8933: this.artifact = artifact; gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: public boolean isAtGauge() { gernotbelger@8933: return this.atGauge; gernotbelger@8933: } gernotbelger@8933: gernotbelger@9040: public String getMetadata() { gernotbelger@9040: gernotbelger@9040: switch (this.mode) { gernotbelger@9040: case QFREE: gernotbelger@9040: case QGAUGE: { gernotbelger@9040: final String data = getMetadataQ(); gernotbelger@9040: if (data == null) gernotbelger@9040: return null; gernotbelger@9040: gernotbelger@9040: return Resources.getMsg(this.context.getMeta(), CSV_META_Q, CSV_META_Q, data); gernotbelger@9040: } gernotbelger@9040: gernotbelger@9040: // TODO: probably none, default is wrong here, but this how it was implemented in WaterlevelExporter. gernotbelger@9040: case WFREE: gernotbelger@9040: case WGAUGE: gernotbelger@9040: case NONE: gernotbelger@9040: default: gernotbelger@9040: final double[] ws = RiverUtils.getWs(this.artifact); gernotbelger@9040: gernotbelger@9040: if (ws == null || ws.length <= 0) gernotbelger@9040: return null; gernotbelger@9040: gernotbelger@9040: // FIXME: we also have here a case single?! gernotbelger@9040: gernotbelger@9040: // FIXME: use correct wst unit! gernotbelger@9040: gernotbelger@9040: // FIXME: bad formatting ofd values. Use the correct Formatter! gernotbelger@9040: final String lower = String.valueOf(ws[0]); gernotbelger@9040: final String upper = String.valueOf(ws[ws.length - 1]); gernotbelger@9040: gernotbelger@9040: return Resources.getMsg(this.context.getMeta(), CSV_META_W, CSV_META_W, lower, upper); gernotbelger@9040: } gernotbelger@9040: } gernotbelger@9040: gernotbelger@9040: private String getMetadataQ() { gernotbelger@9040: gernotbelger@9040: final double[] qs = RiverUtils.getQs(this.artifact); gernotbelger@9040: if (qs == null || qs.length == 0) gernotbelger@9040: return null; gernotbelger@9040: gernotbelger@9040: final RiverUtils.WQ_INPUT input = RiverUtils.getWQInputMode(this.artifact); gernotbelger@9040: gernotbelger@9040: switch (input) { gernotbelger@9040: case ADAPTED: gernotbelger@9040: case RANGE: gernotbelger@9040: return String.valueOf(qs[0]) + " - " + String.valueOf(qs[qs.length - 1]); gernotbelger@9040: gernotbelger@9040: case SINGLE: gernotbelger@9040: String data = String.valueOf(qs[0]); gernotbelger@9040: for (int i = 1; i < qs.length; i++) gernotbelger@9040: data += ", " + String.valueOf(qs[i]); gernotbelger@9040: return data; gernotbelger@9040: gernotbelger@9040: default: gernotbelger@9040: return null; gernotbelger@9040: } gernotbelger@9040: } gernotbelger@9040: gernotbelger@8933: public String getColumnHeader() { gernotbelger@8933: gernotbelger@8933: if (!this.atGauge) gernotbelger@8933: return null; gernotbelger@8933: gernotbelger@8933: // REMARK: bad inter-dependency to WaterlevelExporter, but we want to really copy the logic from WInfo gernotbelger@8933: if (this.isQ) gernotbelger@8933: return Resources.getMsg(this.context.getMeta(), CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER); gernotbelger@8933: gernotbelger@8933: return Resources.getMsg(this.context.getMeta(), CSV_W_DESC_HEADER, DEFAULT_CSV_W_DESC_HEADER); gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: public String getDesc(final WQKms wqkms) { gernotbelger@8933: return getDesc(wqkms, this.isQ); gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: public String getDesc(final WQKms wqkms, final boolean isQoverride) { gernotbelger@8933: String colDesc = ""; gernotbelger@8933: gernotbelger@8933: if (this.artifact instanceof WINFOArtifact && isQoverride) { gernotbelger@8933: colDesc = getCSVRowTitle((WINFOArtifact) this.artifact, wqkms); gernotbelger@8933: } else if (!isQoverride) { gernotbelger@8933: final Double value = RiverUtils.getValueFromWQ(wqkms); gernotbelger@8933: colDesc = (value != null) ? Formatter.getWaterlevelW(this.context).format(value) : null; gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: if (this.artifact instanceof WINFOArtifact) { gernotbelger@8933: if (wqkms != null && wqkms.getRawValue() != null) { gernotbelger@8933: final WINFOArtifact winfo = (WINFOArtifact) this.artifact; gernotbelger@8933: colDesc = RiverUtils.getNamedMainValue(winfo, wqkms.getRawValue()); gernotbelger@8933: // For 'W am Pegel' s gernotbelger@8933: if (colDesc == null) { gernotbelger@8933: final Double value = RiverUtils.getValueFromWQ(wqkms); gernotbelger@8933: colDesc = (value != null) ? Formatter.getWaterlevelW(this.context).format(value) : null; gernotbelger@8933: } gernotbelger@8933: } gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: if (colDesc == null) gernotbelger@8933: return ""; gernotbelger@8933: gernotbelger@8933: /* gernotbelger@8933: * Quick hack. Can be removed when database strings are gernotbelger@8933: * adapted or left in here as it should never be harmful. gernotbelger@8933: */ gernotbelger@8933: return colDesc.replace("Amtl.Festlegung_", "Amtl. "); gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: private String getCSVRowTitle(final WINFOArtifact winfo, final WQKms wqkms) { gernotbelger@8933: gernotbelger@8933: if (this.mode == WQ_MODE.WFREE || this.mode == WQ_MODE.QGAUGE) gernotbelger@8933: return localizeWQKms(wqkms); gernotbelger@8933: gernotbelger@8933: final Double v = wqkms.getRawValue(); gernotbelger@8933: gernotbelger@8933: final String nmv = RiverUtils.getNamedMainValue(winfo, v); gernotbelger@8933: gernotbelger@8933: if (nmv != null && nmv.length() > 0) { gernotbelger@8933: return RiverUtils.stripNamedMainValue(nmv); gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: return localizeWQKms(wqkms); gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: /** gernotbelger@8933: * Get a string like 'W=' or 'Q=' with a number following in localized gernotbelger@8933: * format. gernotbelger@8933: */ gernotbelger@8933: private String localizeWQKms(final WQKms wqkms) { gernotbelger@8933: final Double rawValue = wqkms.getRawValue(); gernotbelger@8933: gernotbelger@8933: if (rawValue == null) { gernotbelger@8933: return wqkms.getName(); gernotbelger@8933: } gernotbelger@8933: gernotbelger@8933: final NumberFormat nf = Formatter.getRawFormatter(this.context); gernotbelger@8933: gernotbelger@8933: if (this.mode == WQ_MODE.WFREE || this.mode == WQ_MODE.WGAUGE) gernotbelger@8933: return "W=" + nf.format(rawValue); gernotbelger@8933: gernotbelger@8933: return "Q=" + nf.format(rawValue); gernotbelger@8933: } gernotbelger@8933: }