org.deegree.tools.rendering.manager.DataManager.java Source code

Java tutorial

Introduction

Here is the source code for org.deegree.tools.rendering.manager.DataManager.java

Source

//$HeadURL$
/*----------------------------------------------------------------------------
 This file is part of deegree, http://deegree.org/
 Copyright (C) 2001-2009 by:
 Department of Geography, University of Bonn
 and
 lat/lon GmbH
    
 This library is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free
 Software Foundation; either version 2.1 of the License, or (at your option)
 any later version.
 This library is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 details.
 You should have received a copy of the GNU Lesser General Public License
 along with this library; if not, write to the Free Software Foundation, Inc.,
 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 Contact information:
    
 lat/lon GmbH
 Aennchenstr. 19, 53177 Bonn
 Germany
 http://lat-lon.de/
    
 Department of Geography, University of Bonn
 Prof. Dr. Klaus Greve
 Postfach 1147, 53001 Bonn
 Germany
 http://www.geographie.uni-bonn.de/deegree/
    
 e-mail: info@deegree.org
 ----------------------------------------------------------------------------*/

package org.deegree.tools.rendering.manager;

import static org.deegree.db.ConnectionProviderUtils.getSyntheticProvider;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.MissingFormatArgumentException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.deegree.commons.annotations.Tool;
import org.deegree.commons.tools.CommandUtils;
import org.deegree.commons.utils.ArrayUtils;
import org.deegree.db.ConnectionProvider;
import org.deegree.db.ConnectionProviderProvider;
import org.deegree.services.wpvs.exception.DatasourceException;
import org.deegree.services.wpvs.io.BackendResult;
import org.deegree.services.wpvs.io.ModelBackend;
import org.deegree.services.wpvs.io.ModelBackend.Type;
import org.deegree.services.wpvs.io.file.FileBackend;
import org.deegree.tools.rendering.manager.buildings.BuildingManager;
import org.deegree.tools.rendering.manager.buildings.PrototypeManager;
import org.deegree.tools.rendering.manager.stage.StageManager;
import org.deegree.tools.rendering.manager.trees.TreeManager;
import org.deegree.workspace.ResourceLocation;
import org.deegree.workspace.Workspace;
import org.deegree.workspace.standard.DefaultWorkspace;

/**
 * The <code>DataManager</code> is the user interface to the WPVS model backend. It can insert, update and delete
 * following data models:
 * <ul>
 * <li>Trees</li>
 * <li>Buildings</li>
 * <li>Prototypes - which are buildings</li>
 * <li>Stages - which will be handled as prototype references.</li>
 * </ul>
 * 
 * 
 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
 * @author last edited by: $Author$
 * @version $Revision$, $Date$
 * 
 */
@Tool("The DataManager inserts, updates and deletes 3d-objects in/from the WPVS backend")
public class DataManager {

    /**
     * The <code>Action</code> the datamanager may perform
     * 
     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
     * @author last edited by: $Author$
     * @version $Revision$, $Date$
     * 
     */
    public enum Action {
        /**
         * Perform import action
         */
        IMPORT,
        /**
         * perform delete action.
         */
        DELETE;

        @Override
        public String toString() {
            return this.name().toLowerCase();
        }
    }

    private static final String ACTION = "action";

    private static final String DB_HOST = "hosturl";

    private static final String OPT_FILE_BACKEND_DIR = "file_backend_dir";

    private static final String OPT_DB_USER = "user";

    private static final String OPT_DB_PASS = "password";

    private static final String OPT_FILE = "file";

    private static final String OPT_TEXTURE_DIR = "texturedir";

    private static final String TYPE = "type";

    private static final String QL = "qualitylevel";

    private static final String OPT_CREATE_LOWEST_LEVELS = "create_lowest_levels";

    private static final String OPT_WPVS_TRANSLATION_TO = "wpvs_translation";

    private static final String OPT_DELETE_SQL = "sqlWhere";

    /*
     * VRML file handling options
     */
    /**
     * The commandline argument for the building id
     */
    public static final String OPT_UUID = "uuid";

    /**
     * The commandline argument for the translation in x direction.
     */
    public static final String OPT_VRML_TRANSLATION_X = "vrml_xTranslation";

    /**
     * The commandline argument for the translation in y direction.
     */
    public static final String OPT_VRML_TRANSLATION_Y = "vrml_yTranslation";

    /**
     * The commandline argument for the translation in z direction.
     */
    public static final String OPT_VRML_TRANSLATION_Z = "vrml_zTranslation";

    /**
     * The commandline argument for the flipping of y and z
     */
    public static final String OPT_VRML_FLIP_Y_Z = "vrml_flip_y_z";

    /**
     * The commandline argument for the maximum size of a texture.
     */
    public static final String OPT_VRML_MAX_TEX_DIM = "vrml_texture_dimension";

    /**
     * The commandline argument for the rotation axis definition, a comma separated list of 4 values, x,y,z,a
     */
    public static final String OPT_VRML_ROTATION_AXIS = "vrml_rotation_axis";

    /**
     * The commandline argument for the schemalocation of a citygml file
     */
    public static final String OPT_CITY_GML_SCHEMA = "citygml_schema_location";

    /**
     * The commandline argument for the building color of a citygml file
     */
    public static final String OPT_CITY_GML_COLOR = "citygml_building_color";

    /**
     * The commandline argument for using the opengis namespace or the citygml namespace.
     */
    public static final String OPT_USE_OPENGIS = "use_opengis_ns";

    private static Workspace workspace;

    /**
     * Creates the commandline parser and adds the options.
     * 
     * @param args
     */
    public static void main(String[] args) {
        CommandLineParser parser = new PosixParser();

        Options options = initOptions();
        boolean verbose = false;

        // for the moment, using the CLI API there is no way to respond to a
        // help argument; see
        // https://issues.apache.org/jira/browse/CLI-179
        if (args.length == 0 || (args.length > 0 && (args[0].contains("help") || args[0].contains("?")))) {
            printHelp(options);
        }

        workspace = new DefaultWorkspace(new File("nix"));

        try {
            CommandLine line = parser.parse(options, args);
            verbose = line.hasOption(CommandUtils.OPT_VERBOSE);
            startManager(line);
        } catch (ParseException exp) {
            System.err.println("ERROR: Invalid command line: " + exp.getMessage());
        } catch (Exception e) {
            System.err.println("ERROR: " + e.getMessage());
            if (verbose) {
                e.printStackTrace();
            }
            System.exit(1);
        }
        // System.exit( 0 );
    }

    private static void startManager(CommandLine line)
            throws FileNotFoundException, IOException, UnsupportedOperationException, DatasourceException {

        ModelBackend<?> backend = getModelBackend(line);

        Action action = null;
        try {
            action = Action.valueOf(line.getOptionValue(ACTION).toUpperCase().trim());
        } catch (Exception e) {
            throw new IllegalArgumentException(ACTION + " may only be " + Arrays.toString(Action.values()), e);
        }
        Type type = null;
        try {
            type = Type.valueOf(line.getOptionValue(TYPE).toUpperCase().trim());
        } catch (Exception e) {
            throw new IllegalArgumentException(TYPE + " may only be one of " + Arrays.toString(Type.values()), e);
        }

        int qualityLevel = -1;
        try {
            qualityLevel = Integer.parseInt(line.getOptionValue(QL));
        } catch (NumberFormatException e) {
            // nothing
        }
        double[] translationVector = createTranslationVector(line.getOptionValue(OPT_WPVS_TRANSLATION_TO));

        backend.setWPVSTranslationVector(translationVector);

        String textureDir = line.getOptionValue(OPT_TEXTURE_DIR);
        String buildingID = line.getOptionValue(OPT_UUID);

        ModelManager<?> manager;
        switch (type) {
        case TREE:
            manager = new TreeManager(backend, textureDir, translationVector);
            break;
        case STAGE:
            manager = new StageManager(backend, 6, qualityLevel, translationVector);
            break;
        case PROTOTYPE:
            manager = new PrototypeManager(backend, textureDir, buildingID, translationVector);
            break;
        default:
            if (Action.IMPORT == action && (qualityLevel < 1) || (qualityLevel > 5)) {
                throw new IllegalArgumentException(QL + " may only be an integer in the range of 1-5.");
            }
            manager = new BuildingManager(backend, textureDir, 6, qualityLevel, buildingID,
                    line.hasOption(OPT_CREATE_LOWEST_LEVELS), translationVector);
        }
        if (action == Action.IMPORT) {
            if (textureDir == null) {
                System.out.println("You supplied no texture dir, using user home/textures directory instead.");
                textureDir = System.getProperty("user.home") + "/textures";
            }
            String file = line.getOptionValue(OPT_FILE);
            if (file == null) {
                throw new IllegalArgumentException(
                        "Missing parameter: " + OPT_FILE + " it is required for importing.");
            }
            File f = new File(file);
            if (!f.exists()) {
                throw new IllegalArgumentException(OPT_FILE + ": " + f.getAbsolutePath()
                        + ", did not denote an existing file, nothing to import.");
            }

            long begin = System.currentTimeMillis();

            BackendResult inserted = manager.importFromFile(f, line);

            System.out
                    .println("Result " + inserted + "\ntook:  " + (System.currentTimeMillis() - begin) + " millis");

        } else {
            String sqlWhere = line.getOptionValue(OPT_DELETE_SQL);
            if (sqlWhere == null || "".equals(sqlWhere)) {
                throw new MissingFormatArgumentException("The sql where clause of the delete action is mandatory.");
            }
            System.out
                    .println("Deleted: " + manager.delete(buildingID, type, qualityLevel, sqlWhere) + " objects.");
        }

    }

    private static ModelBackend<?> getModelBackend(CommandLine line)
            throws UnsupportedOperationException, DatasourceException {

        String testFileBackend = line.getOptionValue(DB_HOST);
        String hostURL = "1";
        String fileBackendDir = null;
        if (testFileBackend.toLowerCase().contains("filebackend")) {
            hostURL = testFileBackend;
            fileBackendDir = line.getOptionValue(OPT_FILE_BACKEND_DIR);
            if (fileBackendDir == null || "".equals(fileBackendDir)) {
                throw new IllegalArgumentException("The filebackend must be supplied with a directory.");
            }
            // TODO integrate this properly
            try {
                File objectsFile = new File(fileBackendDir + "/objects");
                File prototypesFile = new File(fileBackendDir + "/prototypes");
                FileBackend.initFiles(objectsFile);
                FileBackend.initFiles(prototypesFile);
                return new FileBackend(objectsFile, prototypesFile, null);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                throw new RuntimeException(e.getMessage(), e);
            }
        } else {
            if (workspace.getResource(ConnectionProviderProvider.class, hostURL) == null) {
                ResourceLocation<ConnectionProvider> loc = getSyntheticProvider(hostURL, testFileBackend,
                        line.getOptionValue(OPT_DB_USER), line.getOptionValue(OPT_DB_PASS));
                workspace.getLocationHandler().addExtraResource(loc);
            }
        }
        ModelBackend<?> result = ModelBackend.getInstance(hostURL, workspace);
        return result;
    }

    /**
     * @param optionValue
     * @return
     */
    private static double[] createTranslationVector(String optionValue) {
        double[] result = new double[2];
        if (optionValue != null && !"".equals(optionValue)) {
            try {
                result = ArrayUtils.splitAsDoubles(optionValue, ",");
                if (result.length != 2) {
                    throw new NumberFormatException(
                            "Illigal number of values, only two dimensional translations are allowed");
                }
            } catch (NumberFormatException e) {
                System.err.println("Translation vector " + optionValue
                        + " could not be read, please make sure it is a comma seperated list of (floating point) numbers: "
                        + e.getLocalizedMessage());
            }
        }
        return result;
    }

    private static Options initOptions() {
        Options options = new Options();

        Option option = new Option(OPT_FILE.substring(0, 1), OPT_FILE, true, "the file containing data to import");
        option.setArgs(1);
        option.setArgName("relative/absolut file location");
        options.addOption(option);

        option = new Option("ql", QL, true, "defines the quality level of the data the action should operate on");
        option.setArgs(1);
        option.setArgName("[1-5] if " + TYPE + "=prototype, '1' is expected");
        options.addOption(option);

        option = new Option("cll", OPT_CREATE_LOWEST_LEVELS, false,
                "A flag defining if the lowest levels (convexhull, protoype box ref) should be created for the buildings.");
        options.addOption(option);

        option = new Option("td", OPT_TEXTURE_DIR, true, "Directory where texture can be found.");
        option.setArgs(1);
        option.setArgName("path to the directory");
        options.addOption(option);

        option = new Option("id", OPT_UUID, true,
                "ID of the building to delete or of a vrml file, if not provided the file name will be used.");
        option.setArgs(1);
        option.setArgName("The id of the building.");
        options.addOption(option);

        option = new Option("tt", OPT_WPVS_TRANSLATION_TO, true,
                "A comma separated vector, translation vector to the nullpoint of the WPVS (see the WPVS-Config for more information).");
        option.setArgs(1);
        option.setArgName("e.g. -tt \"-2568000,-5606000\"  .");
        options.addOption(option);

        option = new Option("sw", OPT_DELETE_SQL, true,
                "SQL where statement which defines a where cause on the db.");
        option.setArgs(1);
        option.setArgName("a delete sql where statement, e.g. \"WHERE id='building_id'");
        options.addOption(option);

        addActionParameters(options);
        addTypeParameters(options);
        addDatabaseParameters(options);
        addVRMLParameters(options);
        addCityGMLParameters(options);

        CommandUtils.addDefaultOptions(options);

        return options;

    }

    /**
     * @param options
     */
    private static void addActionParameters(Options options) {
        Option option = new Option(ACTION.substring(0, 1), ACTION, true,
                "defines the action the manager should perform");
        option.setArgs(1);
        StringBuilder argNames = new StringBuilder();
        Action[] allActions = Action.values();
        for (int i = 0; i < allActions.length; ++i) {
            Action a = allActions[i];
            argNames.append(a);
            if ((i + 1) < allActions.length) {
                argNames.append("|");
            }
        }
        option.setArgName(argNames.toString());
        option.setRequired(true);
        options.addOption(option);
    }

    /**
     * @param options
     */
    private static void addTypeParameters(Options options) {
        Option option = new Option(TYPE.substring(0, 1), TYPE, true,
                "defines the type of data the manager expects");
        option.setArgs(1);
        StringBuilder argNames = new StringBuilder();
        Type[] allTypes = Type.values();
        for (int i = 0; i < allTypes.length; ++i) {
            Type a = allTypes[i];
            argNames.append(a);
            if ((i + 1) < allTypes.length) {
                argNames.append("|");
            }
        }
        option.setArgName(argNames.toString());
        option.setRequired(true);
        options.addOption(option);
    }

    /**
     * Database parameters
     * 
     * @param options
     *            to add the database option to.
     */
    private static void addDatabaseParameters(Options options) {

        Option option = new Option("host", DB_HOST, true, "url to the database, with or without port");
        option.setArgs(1);
        option.setArgName("for example jdbc:postgresql://dbhost:5432/db_name");
        option.setRequired(true);
        options.addOption(option);

        option = new Option("fbd", OPT_FILE_BACKEND_DIR, true, "directory to be used for the file databackend");
        option.setArgs(1);
        option.setArgName("for example /home/file_backend");
        options.addOption(option);

        option = new Option(OPT_DB_USER.substring(0, 1), OPT_DB_USER, true,
                "username of the database, default will be ${user.name}");
        option.setArgs(1);
        option.setArgName("for example postgres");
        options.addOption(option);

        option = new Option(OPT_DB_PASS.substring(0, 1), OPT_DB_PASS, true,
                "password of the database, default will be empty");
        option.setArgs(1);
        option.setArgName("for example my_secret_password");
        options.addOption(option);

    }

    /**
     * VRML file reading parameters
     * 
     * @param options
     *            to add the vrml options to.
     */
    private static void addVRMLParameters(Options options) {

        Option option = new Option("tx", OPT_VRML_TRANSLATION_X, true,
                "Translation of the x values of the vrml file.");
        option.setArgs(1);
        option.setArgName("Easting translation.");
        options.addOption(option);

        option = new Option("ty", OPT_VRML_TRANSLATION_Y, true, "Translation of the y values of the vrml file.");
        option.setArgs(1);
        option.setArgName("Northing translation.");
        options.addOption(option);

        option = new Option("tz", OPT_VRML_TRANSLATION_Z, true, "Translation of the z values of the vrml file.");
        option.setArgs(1);
        option.setArgName("Up translation.");
        options.addOption(option);

        option = new Option("ra", OPT_VRML_ROTATION_AXIS, true,
                "Rotation axis, comma separated values: x,y,z,angle.");
        option.setArgs(1);
        option.setArgName("Rotation axis.");
        options.addOption(option);

        option = new Option("yz", OPT_VRML_FLIP_Y_Z, false, "Flip the y and z coordinates of the vrml file.");
        options.addOption(option);

        option = new Option("mtd", OPT_VRML_MAX_TEX_DIM, true,
                "The maximum dimension of the textures of the given vrml file, will be 'upped' to the power of two.");
        option.setArgs(1);
        option.setArgName("The maximum dimension of a texture.");
        options.addOption(option);

    }

    /**
     * CityGML file reading parameters
     * 
     * @param options
     *            to add the vrml options to.
     */
    private static void addCityGMLParameters(Options options) {

        Option option = new Option("cgsl", OPT_CITY_GML_SCHEMA, true,
                "Local location of the citygml schema files.");
        option.setArgs(1);
        option.setArgName("File location.");
        options.addOption(option);

        option = new Option("bc", OPT_CITY_GML_COLOR, true,
                "Default color of the citygml buildings in #FFUUFF format.");
        option.setArgs(1);
        option.setArgName("Default color.");
        options.addOption(option);

        option = new Option("ogcns", OPT_USE_OPENGIS, false,
                "Optional flag for using the opengis namespace for citygml buildings.");
        options.addOption(option);

    }

    private static void printHelp(Options options) {
        CommandUtils.printHelp(options, DataManager.class.getCanonicalName(), null, null);
    }

}