view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoExporter.java @ 8980:b194fa64506a

SINFO - show results themes according to spec, either raw data or floating mean values. Some improvements to error handling and handling of empty results.
author gernotbelger
date Thu, 05 Apr 2018 18:30:34 +0200
parents c40db8e8dcae
children 7c1611b5a59e
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.sinfo.common;

import java.io.OutputStream;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.lang.math.DoubleRange;
import org.apache.log4j.Logger;
import org.dive4elements.artifacts.CallMeta;
import org.dive4elements.artifacts.common.utils.Config;
import org.dive4elements.river.FLYS;
import org.dive4elements.river.artifacts.model.CalculationResult;
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.artifacts.sinfo.SInfoI18NStrings;
import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo;
import org.dive4elements.river.artifacts.sinfo.util.MetaAndTableJRDataSource;
import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
import org.dive4elements.river.exports.AbstractExporter;

import au.com.bytecode.opencsv.CSVWriter;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;

/**
 * @author Gernot Belger
 */
public abstract class AbstractSInfoExporter<RESULT extends AbstractSInfoCalculationResult, RESULTS extends AbstractSInfoCalculationResults<RESULT>> extends AbstractExporter {

    private static final String CSV_META_HEADER_SOUNDING = "sinfo.export.flow_depth.csv.meta.header.sounding";

    private static final String CSV_META_HEADER_SOUNDING_YEAR = "sinfo.export.flow_depth.csv.meta.header.sounding.year";

    private static final String CSV_META_HEADER_SOUNDING_TYPE = "sinfo.export.flow_depth.csv.meta.header.sounding.type";

    private static final String CSV_META_HEADER_SOUNDING_EVALUATOR = "sinfo.export.flow_depth.csv.meta.header.sounding.evaluator";

    private static final String CSV_META_HEADER_SOUNDING_PRJ = "sinfo.export.flow_depth.csv.meta.header.sounding.prj";

    private static final String CSV_META_HEADER_SOUNDING_ELEVATIOIN_MODEL = "sinfo.export.flow_depth.csv.meta.header.sounding.elevationmodel";

    private static final String CSV_META_HEADER_SOUNDING_ELEVATIOIN_MODEL_ORIGINAL = "sinfo.export.flow_depth.csv.meta.header.sounding.elevationmodel.original";

    /** The storage that contains the current calculation result. */
    private RESULTS data = null;

    protected abstract Logger getLog();

    public RESULTS getData() {
        return this.data;
    }

    @Override
    protected final void addData(final Object d) {
        /* reset */
        this.data = null;

        if (d instanceof CalculationResult) {

            final Object dat = ((CalculationResult) d).getData();
            if (dat != null) {
                @SuppressWarnings("unchecked")
                final RESULTS result = (RESULTS) dat;
                this.data = result;
            }
        }
    }

    /**
     * Formats header with unit and label: msg [unit] (label)
     */
    protected final String msgUnitLabel(final String key, final String unit, final String label) {
        final String msg = msg(key);
        return String.format("%s [%s] (%s)", msg, unit, label);
    }

    @Override
    protected final void writeCSVData(final CSVWriter writer) {
        getLog().info("writeCSVData");

        /* fetch calculation results */
        final RESULTS results = this.data;

        final RiverInfo river = results.getRiver();

        /* write as csv */
        writeCSVGlobalMetadata(writer, results);
        writeCSVHeader(writer, results, river);

        for (final RESULT result : results.getResults()) {
            writeCSVResult(writer, results, result);
        }
    }

    protected abstract void writeCSVHeader(final CSVWriter writer, final RESULTS results, final RiverInfo river);

    /**
     * Add metadata that is once written to the top of the file.
     */
    protected abstract void writeCSVGlobalMetadata(final CSVWriter writer, final RESULTS results);

    protected final void writeCSVMetaEntry(final CSVWriter writer, final String message, final Object... messageArgs) {

        final CallMeta meta = this.context.getMeta();

        writer.writeNext(new String[] { Resources.getMsg(meta, message, message, messageArgs) });
    }

    protected final void writeCSVResult(final CSVWriter writer, final RESULTS results, final RESULT result) {

        writeCSVResultMetadata(writer, results, result);

        /* nwo the value rows */
        final Collection<SInfoResultRow> rows = result.getRows();
        for (final SInfoResultRow row : rows) {
            writeCSVRow(writer, results, result, row);
        }
    }

    /**
     * Add metadata that is written once per result set.
     */
    protected abstract void writeCSVResultMetadata(CSVWriter writer, RESULTS results, RESULT result);

    protected final void writeCSVRow(final CSVWriter writer, final RESULTS results, final RESULT result, final SInfoResultRow row) {
        getLog().debug("writeCSVFlowDepthRow");

        final String[] formattedRow = formatCSVRow(results, result, row);
        writer.writeNext(formattedRow);
    }

    protected abstract String[] formatCSVRow(RESULTS results, RESULT result, final SInfoResultRow row);

    @Override
    protected final void writePDF(final OutputStream outStream) {
        getLog().debug("write PDF");

        final JRDataSource source = createJRData();

        final String confPath = Config.getConfigDirectory().toString();

        // FIXME: distinguish between with and without tkh: we need two jasper reports!

        final Map<String, Object> parameters = new HashMap<>();
        parameters.put("ReportTitle", "Exported Data");

        try {
            final String jasperPath = confPath + getJasperFile();

            final JasperPrint print = JasperFillManager.fillReport(jasperPath, parameters, source);
            JasperExportManager.exportReportToPdfStream(print, outStream);
        }
        catch (final JRException je) {
            getLog().warn("Error generating PDF Report!", je);
        }
    }

    protected abstract String getJasperFile();

    private JRDataSource createJRData() {

        /* fetch calculation results */
        final RESULTS results = this.data;

        final MetaAndTableJRDataSource source = new MetaAndTableJRDataSource();

        addJRMetaData(source, results);

        for (final RESULT result : results.getResults()) {
            addJRTableData(source, results, result);
        }

        return source;
    }

    protected abstract void addJRMetaData(final MetaAndTableJRDataSource source, final RESULTS results);

    protected final void addJRTableData(final MetaAndTableJRDataSource source, final RESULTS results, final RESULT result) {

        final Collection<SInfoResultRow> rows = result.getRows();

        for (final SInfoResultRow row : rows) {

            final String[] formattedRow = formatPDFRow(results, row);
            source.addData(formattedRow);
        }
    }

    protected abstract String[] formatPDFRow(RESULTS results, final SInfoResultRow row);

    protected final void writeCSVGlobalMetadataDefaults(final CSVWriter writer, final AbstractSInfoCalculationResults<?> results) {

        final String calcModeLabel = results.getCalcModeLabel();
        final RiverInfo river = results.getRiver();
        final DoubleRange calcRange = results.getCalcRange();

        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_HEADER_RESULT, msg(SInfoI18NStrings.CSV_META_HEADER_RESULT_LABEL), river.getName(), calcModeLabel);

        // "# FLYS-Version: "
        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_VERSION, msg(SInfoI18NStrings.CSV_META_VERSION_LABEL), FLYS.VERSION);

        // "# Bearbeiter: "
        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_USER, msg(SInfoI18NStrings.CSV_META_USER_LABEL), results.getUser());

        // "# Datum der Erstellung: "
        final Locale locale = Resources.getLocale(this.context.getMeta());
        final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_CREATION, msg(SInfoI18NStrings.CSV_META_CREATION_LABEL), df.format(new Date()));

        // "# Gewässer: "
        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_RIVER, msg(SInfoI18NStrings.CSV_META_RIVER_LABEL), river.getName());

        // "# Höhensystem des Flusses: "
        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_HEIGHT_UNIT_RIVER, river.getWstUnit());

        // "# Ort/Bereich (km): "
        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_RANGE, msg(SInfoI18NStrings.CSV_META_RANGE_LABEL),
                getKmFormatter().format(calcRange.getMinimumDouble()), getKmFormatter().format(calcRange.getMaximumDouble()));
    }

    protected final void writeCSVSoundingMetadata(final CSVWriter writer, final BedHeightInfo sounding) {
        writeCSVSoundingMetadata(writer, sounding, CSV_META_HEADER_SOUNDING);
    }

    protected final void writeCSVSoundingMetadata(final CSVWriter writer, final BedHeightInfo sounding, final String mainLabel) {
        // "##METADATEN PEILUNG"
        writeCSVMetaEntry(writer, mainLabel);

        // "# Jahr der Peilung: "
        writeCSVMetaEntry(writer, CSV_META_HEADER_SOUNDING_YEAR, Integer.toString(sounding.getYear()));
        // "# Aufnahmeart: "
        writeCSVMetaEntry(writer, CSV_META_HEADER_SOUNDING_TYPE, sounding.getType());
        // "# Auswerter: "
        writeCSVMetaEntry(writer, CSV_META_HEADER_SOUNDING_EVALUATOR, sounding.getEvaluationBy());
        // "# Lagesystem: "
        writeCSVMetaEntry(writer, CSV_META_HEADER_SOUNDING_PRJ, sounding.getLocationSystem());
        // "# Höhensystem: "
        writeCSVMetaEntry(writer, CSV_META_HEADER_SOUNDING_ELEVATIOIN_MODEL, sounding.getCurElevationModelUnit());
        // "# ursprüngliches Höhensystem: "
        writeCSVMetaEntry(writer, CSV_META_HEADER_SOUNDING_ELEVATIOIN_MODEL_ORIGINAL, sounding.getOldElevationModelUnit());
    }

    protected final void writeCSVWaterlevelMetadata(final CSVWriter writer, final WstInfo wst) {
        writeCSVWaterlevelMetadata(writer, wst, SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL);
    }

    protected final void writeCSVWaterlevelMetadata(final CSVWriter writer, final WstInfo wst, final String mainLabel) {
        // "##METADATEN WASSERSPIEGELLAGE"
        writeCSVMetaEntry(writer, mainLabel);

        // "# Bezeichnung der Wasserspiegellage: "
        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL_NAME, wst.getLabel());

        // "# Bezugspegel: "
        writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL_GAUGE, wst.getGauge());

        // "# Jahr/Zeitraum der Wasserspiegellage: "
        final int year = wst.getYear();
        if (year > 0)
            writeCSVMetaEntry(writer, SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL_YEAR, Integer.toString(year));
    }

    protected final void addJRMetaDataDefaults(final MetaAndTableJRDataSource source, final AbstractSInfoCalculationResults<?> results) {

        final RiverInfo river = results.getRiver();
        final String wstUnitName = river.getWstUnit();

        source.addMetaData("header", msg(SInfoI18NStrings.CSV_META_HEADER_RESULT_LABEL));
        source.addMetaData("calcMode", results.getCalcModeLabel());

        source.addMetaData("version_label", msg(SInfoI18NStrings.CSV_META_VERSION_LABEL));
        source.addMetaData("version", FLYS.VERSION);

        source.addMetaData("user_label", msg(SInfoI18NStrings.CSV_META_USER_LABEL));
        source.addMetaData("user", results.getUser());

        final Locale locale = Resources.getLocale(this.context.getMeta());
        final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
        source.addMetaData("date_label", msg(SInfoI18NStrings.CSV_META_CREATION_LABEL));
        source.addMetaData("date", df.format(new Date()));

        source.addMetaData("river_label", msg(SInfoI18NStrings.CSV_META_RIVER_LABEL));
        source.addMetaData("river", river.getName());
        source.addMetaData("river_unit", wstUnitName);

        final DoubleRange calcRange = results.getCalcRange();
        final NumberFormat kmFormatter = getKmFormatter();
        final String rangeValue = String.format("%s - %s", kmFormatter.format(calcRange.getMinimumDouble()), kmFormatter.format(calcRange.getMaximumDouble()));
        source.addMetaData("range_label", msg(SInfoI18NStrings.CSV_META_RANGE_LABEL));
        source.addMetaData("range", rangeValue);
    }
}

http://dive4elements.wald.intevation.org