view flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java @ 1176:28154920e0b3

Introduced the possibility for States to transform/modify input before the data is added to FLYSArtifact. flys-artifacts/trunk@2761 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Thu, 15 Sep 2011 15:05:24 +0000
parents ace192d3283d
children 1b432c260e97
line wrap: on
line source
package de.intevation.flys.artifacts.states;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

import org.apache.log4j.Logger;

import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.simple.SimpleFeatureBuilder;

import de.intevation.artifacts.Artifact;
import de.intevation.artifacts.CallContext;

import de.intevation.artifacts.common.utils.FileTools;

import de.intevation.artifactdatabase.state.Facet;

import de.intevation.flys.model.CrossSectionTrack;
import de.intevation.flys.model.DGM;
import de.intevation.flys.model.Floodplain;
import de.intevation.flys.model.RiverAxis;

import de.intevation.flys.artifacts.FLYSArtifact;
import de.intevation.flys.artifacts.model.CalculationMessage;
import de.intevation.flys.artifacts.model.CalculationResult;
import de.intevation.flys.artifacts.model.FacetTypes;
import de.intevation.flys.artifacts.model.WQKms;
import de.intevation.flys.artifacts.model.WMSLayerFacet;
import de.intevation.flys.artifacts.model.WSPLGENCalculation;
import de.intevation.flys.artifacts.model.WSPLGENJob;
import de.intevation.flys.artifacts.model.WSPLGENReportFacet;
import de.intevation.flys.artifacts.resources.Resources;
import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
import de.intevation.flys.exports.WstWriter;
import de.intevation.flys.utils.FLYSUtils;
import de.intevation.flys.utils.GeometryUtils;
import de.intevation.flys.utils.MapfileGenerator;
import de.intevation.flys.wsplgen.JobObserver;
import de.intevation.flys.wsplgen.Scheduler;


public class FloodMapState
extends      DefaultState
implements   FacetTypes
{
    /** The logger that is used in this state. */
    private static Logger logger = Logger.getLogger(FloodMapState.class);


    public static final String KEEP_ARTIFACT_DIR =
        System.getProperty("flys.uesk.keep.artifactsdir", "false");


    public static final String WSP_ARTIFACT_UUID = "uesk.wsp.artifact";

    public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel";

    public static final String WSPLGEN_PARAMETER_FILE = "wsplgen.par";
    public static final String WSPLGEN_BARRIERS_LINES = "barrier_lines.shp";
    public static final String WSPLGEN_BARRIERS_POLY  = "barrier_polygons.shp";
    public static final String WSPLGEN_AXIS           = "axis.shp";
    public static final String WSPLGEN_QPS            = "qps.shp";
    public static final String WSPLGEN_FLOODPLAIN     = "talaue.shp";
    public static final String WSPLGEN_WSP_FILE       = "waterlevel.wst";
    public static final String WSPLGEN_OUTPUT_FILE    = "wsplgen.shp";

    public static final int WSPLGEN_DEFAULT_OUTPUT = 0;



    /**
     * Inner class that is used to create facets. An instance of this class is
     * used while packaging the data for the WSPLGEN calculation.
     */
    private static class FacetCreator {
        protected FLYSArtifact artifact;
        protected List<Facet>  facets;
        protected String url;
        protected String hash;
        protected String stateId;

        public FacetCreator(FLYSArtifact artifact, String hash, String sId) {
            this.facets     = new ArrayList<Facet>(2);
            this.artifact   = artifact;
            this.hash       = hash;
            this.stateId    = sId;
        }

        protected String getRiver() {
            return artifact.getDataAsString("river");
        }

        protected String getUrl() {
            String url = FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPSERVER_URL);
            url = url + "user-wms";
            return url;
        }

        protected String getSrid() {
            return FLYSUtils.getRiverSrid(artifact);
        }

        protected String getBounds() {
            return GeometryUtils.getRiverBounds(getRiver());
        }

        public List<Facet> getFacets() {
            return facets;
        }

        public void createWSPLGENFacet() {
            WMSLayerFacet wsplgen = new WMSLayerFacet(
                0,
                FLOODMAP_WSPLGEN,
                "Ergebnis der WSPLGEN Berechnung",
                ComputeType.ADVANCE,
                stateId,
                hash,
                getUrl());

            wsplgen.addLayer(
                artifact.identifier() + MapfileGenerator.MS_WSPLGEN_POSTFIX);
            wsplgen.setSrid(getSrid());
            wsplgen.setExtent(getBounds());

            facets.add(wsplgen);
        }

        public void createBarrierFacet() {
            WMSLayerFacet barriers = new WMSLayerFacet(
                1,
                FLOODMAP_WSPLGEN,
                "Rohre/Graeben/Daemme",
                ComputeType.ADVANCE,
                stateId,
                hash,
                getUrl());

            barriers.addLayer(
                artifact.identifier() + MapfileGenerator.MS_BARRIERS_POSTFIX);
            barriers.setSrid(getSrid());
            barriers.setExtent(getBounds());

            facets.add(barriers);
        }
    } // end of FacetCreator



    @Override
    public Object computeAdvance(
        FLYSArtifact artifact,
        String       hash,
        CallContext  context,
        List<Facet>  facets,
        Object       old
    ) {
        logger.debug("FloodMapState.computeAdvance");

        File artifactDir = getDirectory(artifact);

        if (artifactDir == null) {
            logger.error("Could not create directory for WSPLGEN results!");
            return null;
        }

        WSPLGENCalculation calculation = new WSPLGENCalculation();

        FacetCreator facetCreator = new FacetCreator(artifact, hash, getID());

        WSPLGENJob job = prepareWSPLGENJob(
            artifact,
            facetCreator,
            artifactDir,
            context,
            calculation);

        CalculationResult  res   = new CalculationResult(null, calculation);
        WSPLGENReportFacet report= new WSPLGENReportFacet(
            ComputeType.ADVANCE, hash, getID(), res);

        facets.add(report);

        if (job == null) {
            if (KEEP_ARTIFACT_DIR.equals("false")) {
                removeDirectory(artifact);
            }

            calculation.addError(-1, Resources.getMsg(
                context.getMeta(),
                "wsplgen.job.error",
                "wsplgen.job.error"));

            logger.error("No WSPLGEN processing has been started!");

            return null;
        }

        Scheduler scheduler = Scheduler.getInstance();
        scheduler.addJob(job);

        facetCreator.createWSPLGENFacet();

        facets.addAll(facetCreator.getFacets());

        context.afterCall(CallContext.BACKGROUND);
        context.addBackgroundMessage(new CalculationMessage(
            JobObserver.STEPS.length,
            0,
            Resources.getMsg(
                context.getMeta(),
                "wsplgen.job.queued",
                "wsplgen.job.queued")
        ));

        return null;
    }


    /**
     * Returns (and creates if not existing) the directory for storing WSPLEN
     * data for the owner artifact.
     *
     * @param artifact The owner Artifact.
     *
     * @return the directory for WSPLEN data.
     */
    protected File getDirectory(FLYSArtifact artifact) {
        String shapePath = FLYSUtils.getXPathString(
            FLYSUtils.XPATH_SHAPEFILE_DIR);

        File artifactDir = FileTools.getDirectory(
            shapePath, artifact.identifier());

        return artifactDir;
    }


    /**
     * Removes the directory and all its content where the required data and the
     * results of WSPLGEN are stored. Should be called in endOfLife().
     */
    protected void removeDirectory(FLYSArtifact artifact) {
        String shapePath = FLYSUtils.getXPathString(
            FLYSUtils.XPATH_SHAPEFILE_DIR);

        File artifactDir = new File(shapePath, artifact.identifier());

        if (artifactDir.exists()) {
            logger.info("Delete directory: " + artifactDir.getAbsolutePath());
            boolean success = FileTools.deleteRecursive(artifactDir);
        }
        else {
            logger.debug("There is no directory to remove.");
        }
    }


    @Override
    public void endOfLife(Artifact artifact, Object callContext) {
        logger.info("FloodMapState.endOfLife: " + artifact.identifier());

        FLYSArtifact flys = (FLYSArtifact) artifact;
        removeDirectory(flys);
    }


    protected WSPLGENJob prepareWSPLGENJob(
        FLYSArtifact       artifact,
        FacetCreator       facetCreator,
        File               artifactDir,
        CallContext        context,
        WSPLGENCalculation calculation
    ) {
        logger.debug("FloodMapState.prepareWSPLGENJob");

        WSPLGENJob job = new WSPLGENJob(
            artifact,
            artifactDir,
            context,
            calculation);

        File paraFile = new File(artifactDir, WSPLGEN_PARAMETER_FILE);

        setOut(artifact, job);
        setRange(artifact, job);
        setDelta(artifact, job);
        setGel(artifact, job);
        setDist(artifact, job);
        setLine(artifact, facetCreator, artifactDir, job);
        setAxis(artifact, artifactDir, job);
        setPro(artifact, artifactDir, job);
        setDgm(artifact, job);
        setArea(artifact, artifactDir, job);
        setOutFile(artifact, job);
        setWsp(artifact, context, artifactDir, job);    // WSP

        // TODO
        // setWspTag(artifact, job);

        try {
            job.toFile(paraFile);

            return job;
        }
        catch (IOException ioe) {
            logger.warn("Cannot write PAR file: " + ioe.getMessage());
        }
        catch (IllegalArgumentException iae) {
            logger.warn("Cannot write PAR file: " + iae.getMessage());
        }

        return null;
    }


    protected void setOut(FLYSArtifact artifact, WSPLGENJob job) {
        job.setOut(WSPLGEN_DEFAULT_OUTPUT);
    }


    protected void setRange(FLYSArtifact artifact, WSPLGENJob job) {
        double[] range = FLYSUtils.getKmRange(artifact);

        job.setStart(range[0]);
        job.setEnd(range[1]);
    }


    protected void setDelta(FLYSArtifact artifact, WSPLGENJob job) {
        String from = artifact.getDataAsString("diff_from");
        String to   = artifact.getDataAsString("diff_to");
        String diff = artifact.getDataAsString("diff_diff");

        try {
            job.setFrom(Double.parseDouble(from));
        }
        catch (NumberFormatException nfe) {
        }

        try {
            job.setTo(Double.parseDouble(to));
        }
        catch (NumberFormatException nfe) {
        }

        try {
            job.setDiff(Double.parseDouble(diff));
        }
        catch (NumberFormatException nfe) {
        }
    }


    protected void setGel(FLYSArtifact artifact, WSPLGENJob job) {
        String gel = artifact.getDataAsString("use_floodplain");

        if (gel != null && gel.length() > 0) {
            boolean use = Boolean.parseBoolean(gel);

            if (use) {
                job.setGel(WSPLGENJob.GEL_SPERRE);
            }
            else {
                job.setGel(WSPLGENJob.GEL_NOSPERRE);
            }
        }
    }


    protected void setDist(FLYSArtifact artifact, WSPLGENJob job) {
        String dist = artifact.getDataAsString("profile_distance");

        try {
            job.setDist(Double.parseDouble(dist));
        }
        catch (NumberFormatException nfe) {
            // nothing to do here
        }
    }


    protected void setLine(
        FLYSArtifact artifact,
        FacetCreator facetCreator,
        File         dir,
        WSPLGENJob   job
    ) {
        String geoJSON = artifact.getDataAsString("uesk.barriers");
        String srid    = FLYSUtils.getRiverSrid(artifact);
        String srs     = "EPSG:" + srid;

        if (geoJSON == null || geoJSON.length() == 0) {
            logger.debug("No barrier features in parameterization existing.");
            return;
        }

        SimpleFeatureType ft = getBarriersFeatureType(
            "barriers", srs, Geometry.class);

        List<SimpleFeature> features = GeometryUtils.parseGeoJSON(geoJSON, ft);
        if (features == null || features.size() == 0) {
            logger.debug("No barrier features extracted.");
            return;
        }

        FeatureCollection[] fcs = splitLinesAndPolygons(features);

        File shapeLines = new File(dir, WSPLGEN_BARRIERS_LINES);
        File shapePolys = new File(dir, WSPLGEN_BARRIERS_POLY);

        Object[][] obj = new Object[][] {
            new Object[] { "typ", String.class }
        };

        boolean l = GeometryUtils.writeShapefile(
            shapeLines,
            GeometryUtils.buildFeatureType("lines", srs, LineString.class, obj),
            fcs[0]);

        if (l) {
            logger.debug(
                "Successfully created barrier line shapefile. " +
                "Write shapefile path into WSPLGEN job.");
            job.addLin(shapeLines.getAbsolutePath());
        }

        boolean p = GeometryUtils.writeShapefile(
            shapePolys,
            GeometryUtils.buildFeatureType("polygons", srs, Polygon.class, obj),
            fcs[1]);

        if (p) {
            logger.debug(
                "Successfully created barrier polygon shapefile. " +
                "Write shapefile path into WSPLGEN job.");
            job.addLin(shapePolys.getAbsolutePath());
        }

        if (p || l) {
            facetCreator.createBarrierFacet();
        }
    }


    protected SimpleFeatureType getBarriersFeatureType(
        String name,
        String srs,
        Class  type
    ) {
        Object[][] attrs = new Object[2][];
        attrs[0] = new Object[] { "typ", String.class };
        attrs[1] = new Object[] { "elevation", Double.class };

        return GeometryUtils.buildFeatureType(name, srs, type, attrs);
    }


    protected FeatureCollection[] splitLinesAndPolygons(List<SimpleFeature> f) {
        FeatureCollection lines    = FeatureCollections.newCollection();
        FeatureCollection polygons = FeatureCollections.newCollection();

        for (SimpleFeature feature: f) {
            Geometry geom = (Geometry) feature.getDefaultGeometry();


            if (geom instanceof LineString) {
                geom = applyElevationAttribute(feature, (LineString) geom);
                lines.add(feature);
            }
            else if (geom instanceof Polygon) {
                geom = applyElevationAttribute(feature, (Polygon) geom);
                polygons.add(feature);
            }
            else {
                logger.warn("Feature not supported: " + geom.getClass());
            }
        }

        logger.debug("Found " + lines.size() + " barrier lines.");
        logger.debug("Found " + polygons.size() + " barrier polygons.");

        return new FeatureCollection[] { lines, polygons };
    }


    protected static Geometry applyElevationAttribute(
        SimpleFeature feature,
        Geometry      geom
    ) {
        logger.debug("Apply elevations for: " + geom.getClass());

        List<Double> elevations = extractElevations(feature);
        int           numPoints = geom.getNumPoints();
        int        numElevation = elevations.size();

        String typ = (String) feature.getAttribute("typ");

        if (numPoints > numElevation) {
            logger.warn("More vertices in Geometry than elevations given.");
        }

        Coordinate[] c = geom.getCoordinates();
        for (int i = 0; i < numPoints; i++) {
            if (i < numElevation) {
                c[i].z = elevations.get(i);
            }
            else if (typ != null && typ.equals("Graben")) {
                c[i].z = -9999d;
            }
            else {
                c[i].z = 9999d;
            }
        }

        return geom;
    }


    protected static List<Double> extractElevations(SimpleFeature feature) {
        String tmp = (String) feature.getAttribute("elevation");
        String typ = (String) feature.getAttribute("typ");

        String[] elevations = tmp == null ? null : tmp.split(" ");

        int num = elevations != null ? elevations.length : 0;

        List<Double> list = new ArrayList<Double>(num);

        for (int i = 0; i < num; i++) {
            try {
                list.add(Double.parseDouble(elevations[i]));
            }
            catch (NumberFormatException nfe) {
                logger.warn("Error while parsing elevation at pos: " + i);
                if (typ != null && typ.equals("Graben")) {
                    list.add(new Double(-9999.0));
                }
                else {
                    list.add(new Double(9999.0));
                }
            }
        }

        return list;
    }


    protected void setAxis(FLYSArtifact artifact, File dir, WSPLGENJob job) {
        String river = artifact.getDataAsString("river");
        String srid    = FLYSUtils.getRiverSrid(artifact);
        String srs     = "EPSG:" + srid;

        RiverAxis axis = RiverAxis.getRiverAxis(river);
        if (axis == null) {
            logger.warn("Could not find river axis for: '" + river + "'");
            return;
        }

        Geometry geom = axis.getGeom();

        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
            "axis", srs, LineString.class);

        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
        builder.add(geom);

        FeatureCollection collection = FeatureCollections.newCollection();
        collection.add(builder.buildFeature("0"));

        File axisShape = new File(dir, WSPLGEN_AXIS);

        boolean a = GeometryUtils.writeShapefile(
            axisShape,
            GeometryUtils.buildFeatureType("axis", srs, LineString.class),
            collection);

        if (a) {
            job.setAxis(axisShape.getAbsolutePath());
        }
    }


    protected void setPro(FLYSArtifact artifact, File dir, WSPLGENJob job) {
        String river = artifact.getDataAsString("river");
        String srid    = FLYSUtils.getRiverSrid(artifact);
        String srs     = "EPSG:" + srid;

        List<CrossSectionTrack> cst =
            CrossSectionTrack.getCrossSectionTrack(river);

        logger.debug("Found " + cst.size() + " CrossSectionTracks.");

        Object[][] attrs = new Object[2][];
        attrs[0] = new Object[] { "ELEVATION", Double.class };
        attrs[1] = new Object[] { "KILOMETER", Double.class };

        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
            "qps", srs, LineString.class, attrs);

        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
        FeatureCollection collection = FeatureCollections.newCollection();

        int i = 0;
        for (CrossSectionTrack track: cst) {
            builder.reset();
            builder.add(track.getGeom());
            builder.add(track.getZ().doubleValue());
            builder.add(track.getKm().doubleValue());

            collection.add(builder.buildFeature(String.valueOf(i++)));
        }

        File qpsShape = new File(dir, WSPLGEN_QPS);

        boolean q = GeometryUtils.writeShapefile(
            qpsShape,
            GeometryUtils.buildFeatureType("qps", srs, LineString.class, attrs),
            collection);

        if (q) {
            job.setPro(qpsShape.getAbsolutePath());
        }
    }


    protected void setDgm(FLYSArtifact artifact, WSPLGENJob job) {
        String   river = artifact.getDataAsString("river");
        double[] range = FLYSUtils.getKmRange(artifact);

        DGM dgm = DGM.getDGM(river, range[0], range[1]);

        if (dgm == null) {
            logger.warn(
                "Could not find a DGM for river '" + river + "'" +
                "(" + range[0] + " - " + range[1] + ")");

            return;
        }

        job.setDgm(dgm.getPath());
    }


    protected void setArea(FLYSArtifact artifact, File dir, WSPLGENJob job) {
        String river = artifact.getDataAsString("river");
        String srid  = FLYSUtils.getRiverSrid(artifact);
        String srs   = "EPSG:" + srid;

        Floodplain plain = Floodplain.getFloodplain(river);

        SimpleFeatureType ft = GeometryUtils.buildFeatureType(
            "talaue", srs, MultiPolygon.class);

        SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
        builder.add(plain.getGeom());

        FeatureCollection collection = FeatureCollections.newCollection();
        collection.add(builder.buildFeature("0"));

        File talaueShape = new File(dir, WSPLGEN_FLOODPLAIN);

        boolean t = GeometryUtils.writeShapefile(
            talaueShape,
            GeometryUtils.buildFeatureType("talaue", srs, MultiPolygon.class),
            collection);

        if (t) {
            job.setArea(talaueShape.getAbsolutePath());
        }
    }


    protected void setOutFile(FLYSArtifact artifact, WSPLGENJob job) {
        job.setOutFile(WSPLGEN_OUTPUT_FILE);
    }


    protected void setWsp(
        FLYSArtifact artifact,
        CallContext  context,
        File         dir,
        WSPLGENJob   job)
    {
        logger.debug("FloodMapState.setWsp");

        String otherArtifact = artifact.getDataAsString(WSP_ARTIFACT_UUID);
        FLYSArtifact     src = otherArtifact != null
            ? FLYSUtils.getArtifact(otherArtifact, context)
            : artifact;

        CalculationResult rawData = (CalculationResult) src.compute(
            context,
            null,
            WINFO_WSP_STATE_ID,
            ComputeType.ADVANCE,
            false);

        if (rawData == null) {
            logger.warn("No WST data found!");
            return;
        }

        WQKms[]    data = (WQKms[]) rawData.getData();
        WstWriter writer = new WstWriter(data.length);

        // TODO REMOVE job.setWspTag(...) This is only used until the user is
        // able to select the WSP column himself!
        boolean writeWspTag = true;

        double[] buf = new double[4];
        for (WQKms wqkms: data) {
            logger.debug("Add WST column: " + wqkms.getName());
            writer.addColumn(wqkms.getName());

            if (writeWspTag) {
                job.setWspTag(wqkms.getName());
                writeWspTag = false;
            }

            for (int i = 0, num = wqkms.size(); i < num; i++) {
                wqkms.get(i, buf);
                writer.add(buf);
            }
        }

        FileOutputStream fout = null;

        try {
            File wspFile = new File(dir, WSPLGEN_WSP_FILE);
            fout         = new FileOutputStream(wspFile);

            writer.write(fout);

            job.setWsp(wspFile.getAbsolutePath());
        }
        catch (FileNotFoundException fnfe) {
            logger.warn("Error while writing wsp file: " + fnfe.getMessage());
        }
        finally {
            if (fout != null) {
                try {
                    fout.close();
                }
                catch (IOException ioe) { /* do nothing */ }
            }
        }
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org