view src/java/de/intevation/mxd/writer/MapScriptWriter.java @ 256:4dc2b9aa3c57

Added LGPL header.
author raimund renkert <raimund.renkert@intevation.de>
date Mon, 15 Aug 2011 15:03:44 +0200
parents cedb23deb057
children 1ac277053049
line wrap: on
line source
/*
 * Copyright (c) 2011 by Intevation GmbH, Germany <info@intevation.de>
 *
 * This file is part of MXD2map.
 *
 * This program is free software under the LGPL (>=v2.1)
 * Read the file LICENCE.txt coming with the software for details
 * or visit http://www.gnu.org/licenses/ if it does not exist.
 *
 * MXD2map has been developed on behalf of the
 * Bundesamt fuer Seeschifffahrt und Hydrographie (BSH) in Hamburg
 * by Intevation GmbH.
 *
 * Authors:
 * Raimund Renkert <raimund.renkert@intevation.de>
 * Bjoern Schilberg <bjoern.schilberg@intevation.de>
 * Stephan Holl <stephan.holl@intevation.de>
 */

package de.intevation.mxd.writer;

import org.apache.log4j.Logger;

import java.awt.Color;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.io.File;

import javax.xml.xpath.XPathConstants;

import edu.umn.gis.mapscript.mapObj;
import edu.umn.gis.mapscript.layerObj;
import edu.umn.gis.mapscript.classObj;
import edu.umn.gis.mapscript.labelObj;
import edu.umn.gis.mapscript.colorObj;
import edu.umn.gis.mapscript.fontSetObj;
import edu.umn.gis.mapscript.hashTableObj;

import edu.umn.gis.mapscript.MS_UNITS;
import edu.umn.gis.mapscript.MS_LAYER_TYPE;
import edu.umn.gis.mapscript.MS_CONNECTION_TYPE;
import edu.umn.gis.mapscript.MS_FONT_TYPE;
import edu.umn.gis.mapscript.MS_POSITIONS_ENUM;

import de.intevation.mxd.utils.XMLUtils;

/**
 * The Mapfile Writer.
 * This Writer uses the MapScript Java API to create Mapfiles from a DOM.
 *
 * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
 */
public class MapScriptWriter
implements IWriter
{
    /**
     * The Logger.
     */
    private static final Logger logger = Logger.getLogger(MapScriptWriter.class);

    /**
     * Private member.
     */
    private Document root;
    private mapObj map;
    private String mapFilename;
    private String MS_BINDIR = "c:/ms_6.1-dev/bin";
    private String prefix = "";
    private MS_UNITS units = MS_UNITS.MS_METERS;

    /**
     * Default constructor.
     * Creates a mapscript writer object with an empty map.
     */
    public MapScriptWriter() {
        map = new mapObj("");
        mapFilename = "";
    }

    /**
     * Contructor with template and output filename.
     * Creates a mapscript writer object with the given template that saves the
     * map to the filename.
     *
     * @param templ    Mapfile template.
     * @param filename Output file name.
     */
    public MapScriptWriter(String templ, String filename) {
        String path = System.getProperty("user.dir");
        map = new mapObj(templ);
        File f = new File(filename);
        mapFilename = filename;
        if(f.isAbsolute()) {
            map.setMappath(mapFilename);
        }
        else {
            map.setMappath(
                System.getProperty("user.dir") +
                File.separator +
                mapFilename);
            mapFilename = System.getProperty("user.dir") +
                File.separator +
                mapFilename;
        }
        String fontSetPath = map.getFontset().getFilename();
        File fonts = new File(fontSetPath);
        String fontSet = fonts.getAbsolutePath();
        fontSet = fontSet.replaceAll("\\\\", "/");
        map.setFontSet(fontSet);
    }

    /**
     * Write the mapfile.
     * @param doc The root document containin the map attributes.
     *
     * @return Currently always true.
     */
    public boolean write(Document doc) {
        logger.debug("write()");
        this.root = doc;

        //Get the filename.
        Element fileNode = (Element)XMLUtils.xpath(
            root,
            "/mxd/file",
            XPathConstants.NODE);
        //Write the map attributes.
        writeMap();
        //Write the layers.
	boolean ret = writeLayer();
        //Save the map.
	if (ret) {
            mapObj cloneMap = map.cloneMap();
            cloneMap.save(mapFilename);
            logger.info("Mapfile created: " + mapFilename);
            return true;
	}
	return false;
    }

    /**
     * Create the map object and set the attributes.
     */
    private void writeMap() {
        logger.debug("writeMap()");
        //Get the map.
        Element mapNode = (Element)XMLUtils.xpath(
            root,
            "/mxd/map",
            XPathConstants.NODE);

        //Set the name.
        map.setName(mapNode.getAttribute("name"));
        map.setMetaData("wms_title", mapNode.getAttribute("name"));
        //Set the extent.
        map.setExtent(
            Double.parseDouble(mapNode.getAttribute("extent_min_x")),
            Double.parseDouble(mapNode.getAttribute("extent_min_y")),
            Double.parseDouble(mapNode.getAttribute("extent_max_x")),
            Double.parseDouble(mapNode.getAttribute("extent_max_y")));

	map.setMetaData(
	    "ows_extent",
	    mapNode.getAttribute("extent_min_x") + ", " +
	    mapNode.getAttribute("extent_min_y") + ", " +
	    mapNode.getAttribute("extent_max_x") + ", " +
	    mapNode.getAttribute("extent_max_y"));

        //Set the units and projection.
        int proj = Integer.parseInt(mapNode.getAttribute("projection"));
        if(proj != 0) {
            map.setProjection("epsg:" + mapNode.getAttribute("projection"));
        }
        String u = mapNode.getAttribute("units");
        if(u.equals("feet")) {
            units = MS_UNITS.MS_FEET;
        }
        else if(u.equals("inches")) {
            units = MS_UNITS.MS_INCHES;
        }
        else if(u.equals("kilometers")) {
            units = MS_UNITS.MS_KILOMETERS;
        }
        else if(u.equals("meters")) {
            units = MS_UNITS.MS_METERS;
        }
        else if(u.equals("miles")) {
            units = MS_UNITS.MS_MILES;
        }
        else if(u.equals("nauticalmiles")) {
            units = MS_UNITS.MS_NAUTICALMILES;
        }
        else if(u.equals("points")) {
            units = MS_UNITS.MS_PIXELS;
        }
        else if(u.equals("degree")) {
            units = MS_UNITS.MS_DD;
        }
        else {
            units = MS_UNITS.MS_METERS;
        }
        map.setUnits(units);

	String srs = map.getMetaData("ows_srs");
	String mproj = mapNode.getAttribute("projection");
	if(srs.indexOf(mproj) < 0) {
	    srs += " EPSG:" + mproj;
	    map.setMetaData("ows_srs", srs);
	}
    }

    /**
     * Create layer objects and set the attributes.
     */
    private boolean writeLayer() {
        logger.debug("writeLayer()");
        Element mapNode = (Element)XMLUtils.xpath(
            root,
            "/mxd/map",
            XPathConstants.NODE);
        NodeList list = mapNode.getElementsByTagName("layer");
        for(int i = 0; i < list.getLength(); i++) {
            Element layerElement = (Element)list.item(i);
            layerObj layer = new layerObj(map);
            NodeList renderer = layerElement.getElementsByTagName("renderer");
	    if (renderer.getLength() == 0) {
		map.removeLayer(layer.getIndex());
		logger.warn(
		    "Skipping empty layer " + 
		    layerElement.getAttribute("name"));
	        continue;
	    }

            //The layer name.
	    String group = "";
            if(layerElement.hasAttribute("group")) {
		group = layerElement.getAttribute("group");
		String[] splitted = group.split("/");
		String cleangroup = "";
		for(int j = 0; j < splitted.length; j++) {
		    if(!splitted[j].equals("")) {
			splitted[j] = validateString(splitted[j]);
		        cleangroup += "/" + validateLayerName(splitted[j]);
		    }
		}
		group = cleangroup;
            }
	    String lname = layerElement.getAttribute("name");
	    lname = validateLayerName(lname);
	    String ulname = group.replaceAll("/", ".") + "." + lname;
	    if(ulname.startsWith(".")) {
	        ulname = ulname.substring(1);
	    }
            layer.setName(ulname);
            layer.setUnits(units.swigValue());
	    if(!group.equals("")) {
                layer.setMetaData("wms_layer_group", group);
	    }
            layer.setMetaData("wms_title", ulname);

	    if(layerElement.hasAttribute("extent_min_x") &&
	       layerElement.hasAttribute("extent_max_x") &&
	       layerElement.hasAttribute("extent_min_y") &&
	       layerElement.hasAttribute("extent_max_y")) {
	        layer.setMetaData(
		    "ows_extent",
		    layerElement.getAttribute("extent_min_x") + ", " +
		    layerElement.getAttribute("extent_min_y") + ", " +
		    layerElement.getAttribute("extent_max_x") + ", " +
		    layerElement.getAttribute("extent_max_y"));
	    }
            //The layer status.
            String stat = layerElement.getAttribute("status");
            if (stat.equals("on")) {
                layer.setStatus(1);
            }
            else {
                layer.setStatus(0);
            }

            //The scale.
            double maxScale = 0;
            double minScale = 0;
            try {
                maxScale =
                    Double.parseDouble(layerElement.getAttribute("min_scale"));
                minScale =
                    Double.parseDouble(layerElement.getAttribute("max_scale"));
            }
            catch(NumberFormatException nfe) {
                logger.warn("Error setting scale.");
                maxScale = 0;
                minScale = 0;
            }
            layer.setMaxscaledenom(maxScale);
            layer.setMinscaledenom(minScale);

            //The layer type.
            String type = layerElement.getAttribute("type");
            if(type.equals("point")) {
                layer.setType(MS_LAYER_TYPE.MS_LAYER_POINT);
                layer.setTileitem("");
	    }
            else if (type.equals("line")) {
                layer.setType(MS_LAYER_TYPE.MS_LAYER_LINE);
                layer.setTileitem("");
            }
            else if (type.equals("polygon")) {
                layer.setType(MS_LAYER_TYPE.MS_LAYER_POLYGON);
                layer.setTileitem("");
            }
	    else if(type.equals("raster")) {
	        layer.setType(MS_LAYER_TYPE.MS_LAYER_RASTER);
	    }

            //The layer datasource.
            String con_type = layerElement.getAttribute("connection_type");
            if(con_type.equals("local")) {
                // The data source is a local file. (.shp)
                String datasource = "";
                if(layerElement.hasAttribute("workspace")) {
                    datasource = layerElement.getAttribute("workspace");
                    datasource += File.separator;
                }
                datasource += layerElement.getAttribute("data_source");
                datasource = datasource.replaceAll("\\\\", "/");
                layer.setData(datasource);
            }
            else if(con_type.equals("SDE") && !type.equals("raster")) {
                // The data source is a sde database.
                logger.info(
                    "SDE datasource found." +
                    " Please edit password in mapfile.");
                layer.setConnectionType(
                MS_CONNECTION_TYPE.MS_PLUGIN.swigValue(),
                MS_BINDIR + "/ms/plugins/msplugin_sde_93.dll");
		String database_auth = layerElement.getAttribute("database");
		if (database_auth.equals("") || database_auth == null) {
		    logger.warn("Could not set the database connection user." +
				" Setting to \"sde\" as default.");
		    database_auth = "sde";
		}

		String port = layerElement.getAttribute("instance");
                StringBuffer strBuff = new StringBuffer();
                char c;
    
                for (int j = 0; j < port.length() ; j++) {
                    c = port.charAt(j);
        
                    if(Character.isDigit(c)) {
                        strBuff.append(c);
                    }
                }
		
                layer.setConnection(
                layerElement.getAttribute("server") + "," +
                    "port:" + strBuff.toString() + "," +
                database_auth + "," +
                layerElement.getAttribute("user") + ",<PASSWORD>");
                layer.setData(
                    layerElement.getAttribute("data_source") +
                    ",SHAPE," +
                    layerElement.getAttribute("version"));
                layer.setProcessing("CLOSE_CONNECTION=defer");
                layer.setProcessing("ATTRIBUTE_QUALIFIED=TRUE");
                if(layerElement.hasAttribute("join_table")) {
		    logger.info(
		        "The layer " +
			layer.getName() +
			" contains a jointable attribute." +
			" Please verify the attribute parameters.");
		    String join = layerElement.getAttribute("join_table");
		    
                    layer.setProcessing(
                        "JOINTABLE=" +
                    layerElement.getAttribute("join_table"));
                    layer.setFilter(
                        "where " + 
                        layerElement.getAttribute("definition_query") +
                        " AND " +
                        layerElement.getAttribute("join_table_target") + "." +
                        layerElement.getAttribute("join_field_target") + "=" +
                        layerElement.getAttribute("join_table") + "." +
                        layerElement.getAttribute("join_field"));
                }
                else {
                    prefix = layerElement.getAttribute("data_source");
                    if(layerElement.hasAttribute("definition_query") &&
                       !layerElement.getAttribute("definition_query")
                                     .equals("")) {
                        layer.setFilter("where " +
                        layerElement.getAttribute("definition_query"));
                    }
                }
            }
	    else if(con_type.equals("SDE") && type.equals("raster")) {
                // The data source is a sde database.
                logger.info(
                    "SDE datasource found." +
                    " Please edit password in mapfile.");
		String database_auth = layerElement.getAttribute("database");
		if (database_auth.equals("") || database_auth == null) {
		    logger.warn("Could not set the database connection user." +
				" Setting to \"sde\" as default.");
		    database_auth = "sde";
		}

		String port = layerElement.getAttribute("instance");
                StringBuffer strBuff = new StringBuffer();
                char c;
    
                for (int j = 0; j < port.length() ; j++) {
                    c = port.charAt(j);
                    if(Character.isDigit(c)) {
                        strBuff.append(c);
                    }
                }
		
                layer.setData(
                    "SDE:" + layerElement.getAttribute("server") +
                    "," + strBuff +
		    "," + layerElement.getAttribute("database") +
		    "," + layerElement.getAttribute("user") +
		    ",<PASSWORD>," + layerElement.getAttribute("dataset"));
		layer.setType(MS_LAYER_TYPE.MS_LAYER_RASTER);
                if(layerElement.hasAttribute("definition_query") &&
                   !layerElement.getAttribute("definition_query").equals("")) {
                        layer.setFilter("where " +
                        layerElement.getAttribute("definition_query"));
                }
	    }
	    if(!type.equals("raster")) {
                layer.setTemplate("PleaseInsertAValidTemplateForGFI");
	    }

            // Create labelitem
            NodeList labels = layerElement.getElementsByTagName("label");
            if(labels.getLength() > 0) {
                Element label = (Element)labels.item(0);
                String expr = label.getAttribute("expression");
                expr = expr.replaceAll("\\[", "");
                expr = expr.replaceAll("\\]", "");
                if(!prefix.equals("")) {
                    expr = prefix + "." + expr;
                }
                layer.setLabelitem(expr.trim());
            }

            //Write classes.
            writeClass(layer, layerElement);
        }
	return true;
    }

    /**
     * Adds the classes to the layer.
     * @param layer        Mapscript layer object.
     * @param layerElement Dom element containing the class attributes.
     */
    private void writeClass(layerObj layer, Element layerElement) {
        logger.debug("writeClass(layerObj, Element)");
        //Get all renderer elements (renderer in arcgis equals class in the
        //mapfile.)
        NodeList list = layerElement.getElementsByTagName("renderer");

        //Create all found class objects and write the symbols and styles for
        //each class.
        for(int i = 0; i < list.getLength(); i++) {
            Element classElement = (Element)list.item(i);
            classObj co = new classObj(layer);
            String name = classElement.getAttribute("label");
	    name = validateString(name);
            if (name.equals("")) {
                name = layerElement.getAttribute("name");
		name = validateString(name);
                if (list.getLength() > 1) {
                    name += "-" + i;
                }
            }
            co.setName (name);

            //Create label object.
            NodeList labels = layerElement.getElementsByTagName("label");
            if(labels.getLength() > 0) {
                Element labelElement = (Element)labels.item(0);
                String  layerType = layerElement.getAttribute("type");
                writeLabel(co, labelElement, layerType);
            }

            //Create definition expression.
            if(classElement.hasAttribute("field_count")) {
                co.setExpression(createExpression(classElement, i));
            }

            //Write symbols and styles.
            NodeList l = classElement.getChildNodes();
            for (int j = l.getLength() - 1; j >= 0; j--) {
                Element elem = (Element)l.item(j);

                String type = layerElement.getAttribute("type");
                if(type.equals("point") &&
                   elem.getTagName().equals("symbol")) {
                    MarkerStyleWriter swriter =
                        new MarkerStyleWriter (this.map, co);
                    swriter.write (elem);
                }
                else if(type.equals("line") &&
                        elem.getTagName().equals("symbol")) {
                    LineStyleWriter swriter =
                        new LineStyleWriter (this.map, co);
                    swriter.write (elem);
                }
                else if(type.equals("polygon") &&
                        elem.getTagName().equals("symbol")) {
                    FillStyleWriter swriter =
                        new FillStyleWriter (this.map, co);
                    swriter.write (elem);
                }
            }
        }
    }

    /**
     * Create a label object in a class object.
     *
     * @param co Class object.
     * @param labelElement DOM element containing label attributes.
     * @param layertype The layer type. Used to define the label position.
     */
    private void writeLabel(
        classObj co,
        Element labelElement,
        String layerType) {

        //Get the label object. Each class has a predefined labelobject.
        labelObj label = co.getLabel();

        //Get the label text symbol attributes from DOM.
        Element symbol = (Element)labelElement.getFirstChild();
        if(symbol != null && symbol.getTagName().equals("symbol")) {
            String type = symbol.getAttribute("type");
            if(type.equals("text")) {
                label.setType(MS_FONT_TYPE.MS_TRUETYPE);
            }
            label.setSize(Double.parseDouble(symbol.getAttribute("size")));

            if(symbol.hasAttribute("color")) {
                String c = symbol.getAttribute("color");
                Color col = Color.decode(c);
                colorObj color = new colorObj(
                    col.getRed(),
                    col.getGreen(),
                    col.getBlue(),
                    -4);
                label.setColor(color);
            }
            if(symbol.hasAttribute("font")) {
                String font = symbol.getAttribute("font");
                fontSetObj fso = this.map.getFontset();
                hashTableObj fonts = fso.getFonts();
                String mapFont = fonts.get(font,"");
                if(mapFont != null && !mapFont.equals("")) {
                    label.setFont(font);
                }
                else {
                    logger.info(
                        "Could not find font " + font + " in font set. " +
                        "Using FreeSans for labels.");
                        label.setFont("FreeSans");
                }
            }

            if(labelElement.hasAttribute("max_scale")) {
                double min =
                    Double.parseDouble(labelElement.getAttribute("max_scale"));
                if(min > 0) {
                    label.setMinscaledenom(min);
                }
            }
            if(labelElement.hasAttribute("min_scale")) {
                double max =
                    Double.parseDouble(labelElement.getAttribute("min_scale"));
                if(max > 0) {
                    label.setMaxscaledenom(max);
                }
            }

            //Set the label position.
            if(layerType.equals("point")) {
                label.setPosition(MS_POSITIONS_ENUM.MS_UC.swigValue());
            }
        }
    }

    /**
     * Create definition expression.
     *
     * @param ce DOM elementcontaining the class expression information.
     * @param index Index to determine the correct expression operator.
     */
    private String createExpression(Element ce, int index) {
        String expression = "(";
        int count = 0;
        try {
            count = Integer.parseInt(ce.getAttribute("field_count"));
        }
        catch(NumberFormatException nfe) {
            return "";
        }

        if(count == 1) {
           //Create single field expression.
           try {
                //If no exception is thrown, the expression value is a number.
                Double.parseDouble(ce.getAttribute("value"));
                String exp = ce.getAttribute("expression_field_0");
                String pre = "";
                if (!prefix.equals("") && !exp.startsWith(prefix)) {
                    pre = prefix + ".";
                }
                expression += "[" + pre;
                expression += ce.getAttribute("expression_field_0") + "]";
                if(ce.hasAttribute("min_value")) {
                    if(index == 0) {
                        expression += " >= " + ce.getAttribute("min_value");
                    }
                    else {
                        expression += " > " + ce.getAttribute("min_value");
                    }
                    expression += " AND [" + pre;
                    expression += ce.getAttribute("expression_field_0");
                    expression += "]";
                }
                expression += " " + ce.getAttribute("expression_operator");
                expression += " " + ce.getAttribute("value") + ")";
            }
            catch(NumberFormatException nfe) {
                //The expression value is a strings.
                String exp = ce.getAttribute("expression_field_0");
                String pre = "";
                if (!prefix.equals("") && !exp.startsWith(prefix)) {
                    pre = prefix + ".";
                }
                expression += "\"[" + pre;
                expression += ce.getAttribute("expression_field_0") + "]\"";
                expression += " " + ce.getAttribute("expression_operator");
                expression += " \"" + ce.getAttribute("value") + "\")";	
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
        else {
            //Create a multi field expression.
            for (int i = 0; i < count; i++) {
                try {
                    //If no exception is thrown, the expression values are
                    //numbers.
                    Double.parseDouble(ce.getAttribute("value_" + i));
                    String exp = ce.getAttribute("expression_field_" + i);
                    String pre = "";
                    if (!prefix.equals("") && !exp.startsWith(prefix)) {
                       pre = prefix + ".";
                    }
                    expression += "[" + pre;
                    expression += ce.getAttribute("expression_field_" + i);
                    expression += "]";
                    expression += " " + ce.getAttribute("expression_operator");
                    expression += " " + ce.getAttribute("value_" + i);
                    if (i < count - 1) {
                        expression += " AND ";
                    }
                    else {
                        expression += ")";
                    }
                }
                catch (NumberFormatException nfe) {
                    //The expression values are strings.
                    String exp = ce.getAttribute("expression_field_" + i);
                    String pre = "";
                    if (!prefix.equals("") && !exp.startsWith(prefix)) {
                       pre = prefix + ".";
                    }
                    expression += "\"[" + pre;
                    expression += ce.getAttribute("expression_field_" + i);
                    expression += "]\"";
                    expression += " " + ce.getAttribute("expression_operator");
                    expression += " \"" + ce.getAttribute("value_" + i);
                    if (i < count - 1) {
                        expression += "\" AND ";
                    }
                    else {
                        expression += "\")";
                    }
                }
            }
        }
        return expression;
    }

    /**
     * Replaces german umlauts and removes leading and trailing whitespaces.
     *
     * @param s String
     */
    private String validateString (String s) {
        if (s.equals("")) {
            return "";
        }
        String tmp = s.trim();
        tmp = tmp.replace ("ö", "oe");
        tmp = tmp.replace ("ä", "ae");
        tmp = tmp.replace ("ü", "ue");
        tmp = tmp.replace ("ß", "ss");
	if(!s.equals(tmp)) {
	    logger.info("Renamed \"" + s + "\" to \"" + tmp + "\".");
	}
        return tmp;
    }


    /**
     * Replaces special characters and removes leading and trailing whitespaces.
     *
     * @param s String
     *
     * @return the input string without special chars.
     */
    private String validateLayerName (String s) {
    	if(s.equals("")) {
	    return "";
	}

	String tmp = s.trim();
	tmp = tmp.replace(" ", "");
	tmp = tmp.replace("/", "");
	tmp = tmp.replace(",", "");
	tmp = tmp.replace("#", "");
	tmp = tmp.replace("+", "");
	tmp = tmp.replace("~", "");
	tmp = tmp.replace("(", "");
	tmp = tmp.replace(")", "");
	tmp = tmp.replace("{", "");
	tmp = tmp.replace("}", "");
	tmp = tmp.replace("[", "");
	tmp = tmp.replace("]", "");
	tmp = tmp.replace("<", "");
	tmp = tmp.replace(">", "");
	tmp = tmp.replace("*", "");
	tmp = tmp.replace("\\\\", "");
	tmp = tmp.replace("^", "");
	if(!s.equals(tmp)) {
	    logger.info("Renamed \"" + s +"\" to \"" + tmp +"\".");
	}
	return tmp;
    }
}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)