view src/java/de/intevation/mxd/writer/SymbolWriter.java @ 317:5a2b5bc066eb

Implement Label Styles and Labels with background
author Andre Heinecke <aheinecke@intevation.de>
date Thu, 20 Sep 2012 12:55:21 +0200
parents 2cb2f26d0d54
children
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 org.w3c.dom.Element;

import edu.umn.gis.mapscript.mapObj;
import edu.umn.gis.mapscript.classObj;
import edu.umn.gis.mapscript.symbolObj;
import edu.umn.gis.mapscript.symbolSetObj;
import edu.umn.gis.mapscript.lineObj;
import edu.umn.gis.mapscript.pointObj;
import edu.umn.gis.mapscript.fontSetObj;
import edu.umn.gis.mapscript.hashTableObj;
import edu.umn.gis.mapscript.MS_SYMBOL_TYPE;

import java.io.File;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource; 
import java.awt.image.ImageFilter; 
import java.awt.image.ImageProducer; 
import java.awt.image.RGBImageFilter; 
import java.awt.Toolkit;
import java.awt.Color;
import java.awt.Graphics2D;
import javax.imageio.ImageIO;
import java.io.ByteArrayInputStream;

import org.apache.commons.codec.binary.Base64;

/**
 * The interface to the mapfile writer.
 *
 * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
 */
public class SymbolWriter {

    /**
     * The Logger.
     */
    private static final Logger logger = Logger.getLogger(SymbolWriter.class);

    /**
     * Private member.
     */
    private mapObj map;
    private classObj cl;

    /**
     * Contructor with map object and class object.
     *
     * @param map The map object.
     * @param cl  The class object containing the style.
     */
    public SymbolWriter (mapObj map, classObj cl) {
        this.map = map;
        this.cl = cl;
    }

    /**
     * Write the content.
     *
     * @param symbolElement DOM element containg the style and symbol
     * attributes.
     */
    public boolean write(Element symbolElement) {
        logger.debug("write(Element)");
        symbolSetObj symbolSet = map.getSymbolset();

        String symType = symbolElement.getAttribute("style");
        String type = symbolElement.getAttribute("type");
        if(symType.equals("point")) {
            String name = symbolElement.getAttribute("name");
            symbolObj sym = symbolSet.getSymbolByName(name);
            writeSimple(sym);
        }
        else if (symType.equals("arrow")) {
            String name = symbolElement.getAttribute("name");
            symbolObj sym = symbolSet.getSymbolByName(name);
            writeArrow(sym, symbolElement);
        }
        else if (symType.equals("char")) {
            String name = symbolElement.getAttribute("name");
            symbolObj sym = symbolSet.getSymbolByName(name);

            int exists = symbolExists(symbolElement);
            if(exists == -1) {
                String old = symbolElement.getAttribute("name");
                symbolElement.setAttribute(
                    "name",
                    old + symbolSet.getNumsymbols() + 1);
                writeCharacter(sym, symbolElement);
            }
            else {
                symbolElement.setAttribute(
                    "name",
                    symbolSet.getSymbol(exists).getName());
            }
        }
        else if (type.equals("line") && !symType.equals("picture")) {
            String name = symbolElement.getAttribute("name");
            symbolObj sym = symbolSet.getSymbolByName(name);
            writeHatch(sym);
        }
        else if(symType.equals("picture")) {
            // Create the path to the picture.
            int exists = symbolExists(symbolElement);
            String n = symbolElement.getAttribute("name");
            symbolElement.setAttribute(
                "name",
                n + symbolSet.getNumsymbols() + 1);
            String path = this.map.getMappath();
            if (path.endsWith(".map")) {
                path = path.substring(0, path.lastIndexOf(File.separator) + 1);
                path += symbolElement.getAttribute("name") + ".png";
            }
            else {
                path += symbolElement.getAttribute("name") + ".png";
            }
            // Create a new symbol using the path as symbol name.
            // This is a workarround to set the IMAGE attribute to the path
            // since this attribute is immutable via mapscript.
            symbolObj sym = symbolSet.getSymbolByName(path);
            writePicture(sym, symbolElement, path);
        }
        else {
            return false;
        }

        try {
            saveSymbolSet(symbolSet);
        }
        catch(Exception e) {
            logger.error("Error saving symbol set.");
            return false;
        }

        return true;
    }

    /**
     * Create a simple point symbol.
     *
     * @param symbol The symbol object.
     */
    private void writeSimple(symbolObj symbol) {
        logger.debug("writeSimple(symbolObj)");
        lineObj points = new lineObj();
        points.add(new pointObj(1,1,0));
        symbol.setType(MS_SYMBOL_TYPE.MS_SYMBOL_ELLIPSE.swigValue());
        symbol.setPoints(points);
        symbol.setFilled(1);
    }

    /**
     * Create an arrow symbol.
     *
     * @param symbol        The symbol object.
     * @param symbolElement DOM element containing symbol attributes.
     */
    private void writeArrow(symbolObj symbol, Element symbolElement) {
        logger.debug("writeArrow(symbolObj, Element)");
        double len = 0;
        double width = 1;
        try {
            len =
                Double.parseDouble(symbolElement.getAttribute("length"));
            width =
                Double.parseDouble(symbolElement.getAttribute("width"));
        }
        catch(NumberFormatException nfe) {
            logger.warn ("Error setting arrow symbol.");
            return;
        }
        double ratio = len/width;
        lineObj points = new lineObj();

        points.add(new pointObj(0, 0, 0));
        points.add(new pointObj((1*ratio), 0.5, 0));
        points.add(new pointObj(0, 1, 0));
        points.add(new pointObj(0, 0, 0));
        symbol.setType(MS_SYMBOL_TYPE.MS_SYMBOL_VECTOR.swigValue());
        symbol.setPoints(points);
        symbol.setFilled(1);
    }

    /**
     * Create a character marker symbol.
     *
     * @param symbol        The symbol object.
     * @param symbolElement DOM element containing symbol attributes.
     */
    private void writeCharacter(symbolObj symbol, Element symbolElement) {
        logger.debug("writeCharacter(symbolObj, Element)");
        String font = symbolElement.getAttribute("font");
        //Remove all blank character to match the mapserver fonts.txt.
        font = font.replaceAll(" ", "");
        fontSetObj fso = this.map.getFontset();
        hashTableObj fonts = fso.getFonts();
        String mapFont = fonts.get(font,"");
        if (mapFont == null || mapFont.equals("")) {
            logger.warn(
                "Could not find font " + font + " in font set. " +
                "Setting FreeSans as symbol font." +
                "Please add " + font + " to fonts.txt and replace the alias " +
                "in symbol \"" + symbolElement.getAttribute("name") + "\".");
            font = "FreeSans";
        }
        symbol.setName(symbolElement.getAttribute("name"));
        symbol.setFont(font);
        symbol.setType(MS_SYMBOL_TYPE.MS_SYMBOL_TRUETYPE.swigValue());
        symbol.setAntialias(1);
        symbol.setCharacter("&#" + symbolElement.getAttribute("char") + ";");
    }


    /**
     * Create the image and a pixmap symbol.
     *
     * @param sym The symbol object.
     * @param symElem The DOM element containing symbol informations.
     */
    private void writePicture(symbolObj sym, Element symElem, String path) {
        logger.debug("writePicture(symbolObj, Element)");
        try {
            // Create the image as png.
            if(symElem.hasAttribute("picture")) {
                Base64 decoder = new Base64();
                // Decode the base64 string.
                byte[] ba = decoder.decode(symElem.getAttribute("picture"));
                ByteArrayInputStream ins = new ByteArrayInputStream(ba);
                // Create temporary image from byte array.
                BufferedImage bi = ImageIO.read(ins);
                Color c = Color.decode(
                    symElem.getAttribute("transparent_color"));
                // Make a color transparent.
                Image im = colorToTransparent(bi, c);

                // Save image as png.
                BufferedImage dest = new BufferedImage(
                    bi.getWidth(),
                    bi.getHeight(),
                    BufferedImage.TYPE_INT_ARGB); 
                Graphics2D g2 = dest.createGraphics(); 
                g2.drawImage(im, 0, 0, null); 
                g2.dispose(); 
                File f = new File(path);
                ImageIO.write(dest, "PNG", f);
            }
            // Set symbol attributes.
            // Overwrite the symbol name conating the path to the image
            // with the real name. This is part of the workarround(line 137).
            sym.setName(symElem.getAttribute("name"));
            sym.setType(MS_SYMBOL_TYPE.MS_SYMBOL_PIXMAP.swigValue());
        }
        catch(Exception e) {
            logger.warn("Could not save image for pixmap symbol.");
        }
    }


     /**
     * Create a hatch symbol for polygon fill.
     *
     * @param symbol The symbol object.
     */
    private void writeHatch(symbolObj symbol) {
        logger.debug("writeHatch(symbolObj)");
        symbol.setType(MS_SYMBOL_TYPE.MS_SYMBOL_HATCH.swigValue());
    }

    /**
     * Determine whether this writer can create the symbol.
     *
     * @param type The symbol type.
     */
    public boolean canWrite(String type) {
        if (type.equals("point") ||
            type.equals("arrow") ||
            type.equals("char") ||
            type.equals("line") ||
            type.equals("picture")) {
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * Save the symbol set.
     *
     * @param symbols The symbol set object.
     */
    public void saveSymbolSet(symbolSetObj symbols)
    throws Exception {
        String path = this.map.getMappath();
        String filename = path.substring(path.lastIndexOf(File.separator) + 1,
                                         path.lastIndexOf("."));
        String symbolPath = filename + "-symbols.sym";
        if(path.equals("")) {
            return;
        }

        if (path.endsWith(".map")) {
            path = path.substring(0, path.lastIndexOf(File.separator) + 1);
            path += symbolPath;
        }
        else {
            path += symbolPath;
        }
        path = path.replaceAll("\\\\", "/");
        symbols.save(path);
        this.map.setSymbolSet(path);
    }

    /**
     * Determine whether the symbol exists.
     *
     * @param elem DOM element containing the symbol attributes.
     *
     * @return Returns the symbol index if the symbol exists, else returns -1.
     */
    private int symbolExists (Element elem) {
        symbolSetObj symbolSet = map.getSymbolset();
        for (int i = 0; i < symbolSet.getNumsymbols(); i++) {
            symbolObj sym = symbolSet.getSymbol(i);
            int stype = sym.getType();
            String etype = elem.getAttribute("style");
            if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_ELLIPSE.swigValue () &&
                etype.equals("point")) {
                return i;
            }
            else if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_VECTOR.swigValue () &&
                    etype.equals("arrow")) {
                return i;
            }
            else if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_TRUETYPE.swigValue () &&
                    etype.equals("char")) {
                String font = elem.getAttribute("font");
                font = font.replaceAll(" ", "");
                String c = elem.getAttribute("char");
                c = "&#" + c + ";";
                if (font.equals(sym.getFont()) &&
                    c.equals(sym.getCharacter())) {
                    return i;
                }

            }
            else if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_HATCH.swigValue () &&
                    etype.equals("hatch")) {
                return i;
            }
            else if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_PIXMAP.swigValue() &&
                    etype.equals("picture")) {
                return i;        
            }
        }
        return -1;
    }


    /**
     * Transform a color to transparency.
     *
     * @param bi The image.
     * @param col the color.
     *
     * @return The image with transparent color.
     */
    private Image colorToTransparent(BufferedImage bi, Color col) {
        final int markerRGB = col.getRGB() | 0xFF000000;
        ImageFilter filter = new RGBImageFilter() {
            public final int filterRGB(int x, int y, int rgb) {
                if ((rgb | 0xFF000000) == markerRGB) {
                    // Mark the alpha bits as zero - transparent
                    return 0x00FFFFFF & rgb;
                }
                else {
                    // nothing to do
                    return rgb;
                }
            }
        };
        ImageProducer ip = new FilteredImageSource(bi.getSource(), filter);
        return Toolkit.getDefaultToolkit().createImage(ip);
    }
    
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)