view flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java @ 5170:c1b60f8c3390

Made river mapfile generator more tolerant. * Ignores missing river axes. * Ignores invalid river axes. * Ignores invalid geometries.
author Raimund Renkert <rrenkert@intevation.de>
date Mon, 04 Mar 2013 17:11:31 +0100
parents 38ec171544de
children b55975761708
line wrap: on
line source
package de.intevation.flys.utils;

import de.intevation.artifacts.common.utils.Config;
import de.intevation.flys.artifacts.model.LayerInfo;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;

/**
 * This class iterates over a bunch of directories, searches for meta
 * information coresponding to shapefiles and creates a mapfile which is used by
 * a <i>MapServer</i>.
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public abstract class MapfileGenerator
{
    public static final String WSPLGEN_RESULT_SHAPE   = "wsplgen.shp";
    public static final String WSPLGEN_LINES_SHAPE    = "barrier_lines.shp";
    public static final String WSPLGEN_POLYGONS_SHAPE = "barrier_polygons.shp";
    public static final String WSPLGEN_USER_SHAPE     = "user-rgd.shp";

    public static final String WSPLGEN_LAYER_TEMPLATE = "wsplgen_layer.vm";
    public static final String SHP_LAYER_TEMPLATE = "shapefile_layer.vm";
    public static final String DB_LAYER_TEMPLATE  = "db_layer.vm";
    public static final String RIVERAXIS_LAYER_TEMPLATE = "riveraxis-layer.vm";

    public static final String MS_WSPLGEN_PREFIX   = "wsplgen-";
    public static final String MS_BARRIERS_PREFIX  = "barriers-";
    public static final String MS_LINE_PREFIX      = "lines-";
    public static final String MS_POLYGONS_PREFIX  = "polygons-";
    public static final String MS_LAYER_PREFIX     = "ms_layer-";
    public static final String MS_USERSHAPE_PREFIX = "user-";

    private static Logger logger = Logger.getLogger(MapfileGenerator.class);

    private File shapefileDirectory;

    private VelocityEngine velocityEngine;


    protected MapfileGenerator() {
    }


    /**
     * Method to check the existance of a template file.
     *
     * @param templateID The name of a template.
     * @return true, of the template exists - otherwise false.
     */
    public boolean templateExists(String templateID){
        Template template = getTemplateByName(templateID);
        return template != null;
    }


    public abstract void generate() throws Exception;


    /**
     * Returns the VelocityEngine used for the template mechanism.
     *
     * @return the velocity engine.
     */
    protected VelocityEngine getVelocityEngine() {
        if (velocityEngine == null) {
            velocityEngine = new VelocityEngine();
            try {
                setupVelocity(velocityEngine);
            }
            catch (Exception e) {
                logger.error(e, e);
                return null;
            }
        }
        return velocityEngine;
    }


    /**
     * Initialize velocity.
     *
     * @param engine Velocity engine.
     * @throws Exception if an error occured while initializing velocity.
     */
    protected void setupVelocity(VelocityEngine engine)
    throws Exception
    {
        engine.setProperty(
            "input.encoding",
            "UTF-8");

        engine.setProperty(
            RuntimeConstants.RUNTIME_LOG,
            getVelocityLogfile());

        engine.setProperty(
            "resource.loader",
            "file");

        engine.setProperty(
            "file.resource.loader.path",
            getMapserverTemplatePath());

        engine.init();
    }

    protected abstract String getVelocityLogfile();

    protected abstract String getMapserverTemplatePath();

    protected abstract String getMapserverUrl();

    protected VelocityContext getVelocityContext() {
        VelocityContext context = new VelocityContext();

        try {
            context.put("MAPSERVERURL",
                getMapserverUrl());
            context.put("SHAPEFILEPATH",
                getShapefileBaseDir().getCanonicalPath());
            context.put("CONFIGDIR",
                Config.getConfigDirectory().getCanonicalPath());
        }
        catch (FileNotFoundException fnfe) {
            // this is bad
            logger.warn(fnfe, fnfe);
        }
        catch (IOException ioe) {
            // this is also bad
            logger.warn(ioe, ioe);
        }

        return context;
    }


    /**
     * Returns a template specified by <i>model</i>.
     *
     * @param model The name of the template.
     * @return a template.
     */
    protected Template getTemplateByName(String model) {
        if (model.indexOf(".vm") < 0) {
            model = model.concat(".vm");
        }

        try {
            VelocityEngine engine = getVelocityEngine();
            if (engine == null) {
                logger.error("Error while fetching VelocityEngine.");
                return null;
            }

            return engine.getTemplate(model);
        }
        catch (Exception e) {
            logger.warn(e, e);
        }

        return null;
    }


    /**
     * Returns the mapfile  template.
     *
     * @return the mapfile template.
     * @throws Exception if an error occured while reading the configuration.
     */
    protected Template getMapfileTemplateObj()
    throws Exception
    {
        String mapfileName = getMapfileTemplate();
        return getTemplateByName(mapfileName);
    }

    protected abstract String getMapfilePath();

    protected abstract String getMapfileTemplate();


    /**
     * Returns the base directory storing the shapefiles.
     *
     * @return the shapefile base directory.
     *
     * @throws FileNotFoundException if no shapefile path is found or
     * configured.
     */
    public File getShapefileBaseDir()
            throws FileNotFoundException, IOException
    {
        if (shapefileDirectory == null) {
            String path = FLYSUtils.getXPathString(
                FLYSUtils.XPATH_FLOODMAP_SHAPEFILE_DIR);

            if (path != null) {
                shapefileDirectory = new File(path);
            }

            if (shapefileDirectory == null) {
                throw new FileNotFoundException("No shapefile directory given");
            }

            if (!shapefileDirectory.exists()) {
                shapefileDirectory.mkdirs();
            }
        }

        return shapefileDirectory;
    }


    protected File[] getUserDirs()
            throws FileNotFoundException, IOException
    {
        File   baseDir      = getShapefileBaseDir();
        File[] artifactDirs = baseDir.listFiles();

        // TODO ONLY RETURN DIRECTORIES OF THE SPECIFIED USER

        return artifactDirs;
    }



    protected List<String> parseLayers(File[] dirs) {
        List<String> layers = new ArrayList<String>();

        for (File dir: dirs) {
            File[] layerFiles = dir.listFiles(new FilenameFilter() {
                @Override
                public boolean accept(File directory, String name) {
                    return name.startsWith(MS_LAYER_PREFIX);
                }
            });

            for (File layer: layerFiles) {
                try {
                    layers.add(layer.getCanonicalPath());
                }
                catch (IOException ioe) {
                    logger.warn(ioe, ioe);
                }
            }
        }

        return layers;
    }




    /**
     * Creates a layer snippet which might be included in the mapfile.
     *
     * @param layerinfo A LayerInfo object that contains all necessary
     * information to build a Mapserver LAYER section.
     * @param dir The base dir for the LAYER snippet.
     * @param filename The name of the file that is written.
     * @param tpl The Velocity template which is used to create the LAYER
     * section.
     */
    protected void writeLayer(
        LayerInfo layerInfo,
        File      layerFile,
        Template  tpl
    )
    throws    FileNotFoundException
    {
        if (logger.isDebugEnabled()) {
            logger.debug("Write layer for:");
            logger.debug("   directory/file: " + layerFile.getName());
        }

        Writer writer = null;

        try {
            writer = new FileWriter(layerFile);

            VelocityContext context = getVelocityContext();
            context.put("LAYER", layerInfo);

            tpl.merge(context, writer);
        }
        catch (FileNotFoundException fnfe) {
            logger.error(fnfe, fnfe);
        }
        catch (IOException ioe) {
            logger.error(ioe, ioe);
        }
        catch (Exception e) {
            logger.error(e, e);
        }
        finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            }
            catch (IOException ioe) {
                logger.debug(ioe, ioe);
            }
        }
    }


    /**
     * Creates a mapfile with the layer information stored in <i>layers</i>.
     *
     * @param layers Layer information.
     */
    protected void writeMapfile(List<String> layers) {
        String tmpMapName = "mapfile" + new Date().getTime();

        File mapfile = new File(getMapfilePath());

        File   tmp     = null;
        Writer writer  = null;

        try {
            tmp = new File(mapfile.getParent(), tmpMapName);
            tmp.createNewFile();

            writer = new FileWriter(tmp);

            VelocityContext context = getVelocityContext();
            context.put("LAYERS", layers);

            Template mapTemplate = getMapfileTemplateObj();
            if (mapTemplate == null) {
                logger.warn("No mapfile template found.");
                return;
            }

            mapTemplate.merge(context, writer);

            // we need to create a temporary mapfile first und rename it into
            // real mapfile because we don't run into race conditions on this
            // way. (iw)
            tmp.renameTo(mapfile);
        }
        catch (FileNotFoundException fnfe) {
            logger.error(fnfe, fnfe);
        }
        catch (IOException ioe) {
            logger.error(ioe, ioe);
        }
        catch (Exception e) {
            logger.error(e, e);
        }
        finally {
            try {
                if (writer != null) {
                    writer.close();
                }

                if (tmp.exists()) {
                    tmp.delete();
                }
            }
            catch (IOException ioe) {
                logger.debug(ioe, ioe);
            }
        }
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org