rrenkert@243: /* rrenkert@243: * Copyright (c) 2011 by Intevation GmbH, Germany rrenkert@243: * rrenkert@243: * This file is part of MXD2map. rrenkert@243: * rrenkert@243: * This program is free software under the LGPL (>=v2.1) rrenkert@243: * Read the file LICENCE.txt coming with the software for details rrenkert@243: * or visit http://www.gnu.org/licenses/ if it does not exist. rrenkert@243: * rrenkert@243: * MXD2map has been developed on behalf of the rrenkert@243: * Bundesamt fuer Seeschifffahrt und Hydrographie (BSH) in Hamburg rrenkert@243: * by Intevation GmbH. rrenkert@243: * rrenkert@243: * Authors: rrenkert@243: * Raimund Renkert rrenkert@243: * Bjoern Schilberg rrenkert@243: * Stephan Holl rrenkert@243: */ rrenkert@243: rrenkert@87: package de.intevation.mxd.writer; rrenkert@87: rrenkert@87: import org.apache.log4j.Logger; rrenkert@87: rrenkert@87: import org.w3c.dom.Element; rrenkert@87: rrenkert@87: import edu.umn.gis.mapscript.mapObj; rrenkert@87: import edu.umn.gis.mapscript.classObj; rrenkert@87: import edu.umn.gis.mapscript.styleObj; rrenkert@87: import edu.umn.gis.mapscript.symbolObj; rrenkert@87: import edu.umn.gis.mapscript.symbolSetObj; rrenkert@87: import edu.umn.gis.mapscript.lineObj; rrenkert@87: import edu.umn.gis.mapscript.pointObj; raimund@192: import edu.umn.gis.mapscript.fontSetObj; raimund@192: import edu.umn.gis.mapscript.hashTableObj; rrenkert@87: import edu.umn.gis.mapscript.MS_SYMBOL_TYPE; rrenkert@87: raimund@174: import java.io.File; raimund@251: import java.awt.Image; raimund@251: import java.awt.image.BufferedImage; raimund@251: import java.awt.image.DataBufferByte; raimund@251: import java.awt.image.FilteredImageSource; raimund@251: import java.awt.image.ImageFilter; raimund@251: import java.awt.image.ImageProducer; raimund@251: import java.awt.image.RGBImageFilter; raimund@251: import java.awt.Toolkit; raimund@251: import java.awt.Color; raimund@251: import java.awt.Graphics2D; raimund@251: import javax.imageio.ImageIO; raimund@251: import java.io.ByteArrayInputStream; raimund@251: raimund@251: import org.apache.commons.codec.binary.Base64; rrenkert@87: rrenkert@87: /** rrenkert@87: * The interface to the mapfile writer. rrenkert@87: * rrenkert@87: * @author Raimund Renkert rrenkert@87: */ rrenkert@87: public class SymbolWriter { rrenkert@87: rrenkert@87: /** rrenkert@87: * The Logger. rrenkert@87: */ rrenkert@87: private static final Logger logger = Logger.getLogger(SymbolWriter.class); rrenkert@87: rrenkert@181: /** rrenkert@181: * Private member. rrenkert@181: */ rrenkert@87: private mapObj map; rrenkert@87: private classObj cl; rrenkert@87: rrenkert@181: /** rrenkert@181: * Contructor with map object and class object. rrenkert@181: * rrenkert@181: * @param map The map object. rrenkert@181: * @param cl The class object containing the style. rrenkert@181: */ rrenkert@87: public SymbolWriter (mapObj map, classObj cl) { rrenkert@87: this.map = map; rrenkert@87: this.cl = cl; rrenkert@87: } rrenkert@87: rrenkert@87: /** rrenkert@87: * Write the content. rrenkert@181: * rrenkert@181: * @param symbolElement DOM element containg the style and symbol rrenkert@181: * attributes. rrenkert@87: */ rrenkert@120: public boolean write(Element symbolElement) { rrenkert@87: logger.debug("write(Element)"); rrenkert@87: symbolSetObj symbolSet = map.getSymbolset(); rrenkert@87: rrenkert@87: String symType = symbolElement.getAttribute("style"); rrenkert@97: String type = symbolElement.getAttribute("type"); rrenkert@87: if(symType.equals("point")) { raimund@251: String name = symbolElement.getAttribute("name"); raimund@251: symbolObj sym = symbolSet.getSymbolByName(name); rrenkert@87: writeSimple(sym); rrenkert@87: } rrenkert@87: else if (symType.equals("arrow")) { raimund@251: String name = symbolElement.getAttribute("name"); raimund@251: symbolObj sym = symbolSet.getSymbolByName(name); rrenkert@87: writeArrow(sym, symbolElement); rrenkert@87: } rrenkert@87: else if (symType.equals("char")) { raimund@251: String name = symbolElement.getAttribute("name"); raimund@251: symbolObj sym = symbolSet.getSymbolByName(name); raimund@251: vc11884admin@141: int exists = symbolExists(symbolElement); vc11884admin@141: if(exists == -1) { vc11884admin@141: String old = symbolElement.getAttribute("name"); vc11884admin@141: symbolElement.setAttribute( vc11884admin@141: "name", vc11884admin@141: old + symbolSet.getNumsymbols() + 1); vc11884admin@141: writeCharacter(sym, symbolElement); vc11884admin@141: } vc11884admin@141: else { vc11884admin@141: symbolElement.setAttribute( vc11884admin@141: "name", vc11884admin@141: symbolSet.getSymbol(exists).getName()); vc11884admin@141: } rrenkert@87: } raimund@251: else if (type.equals("line") && !symType.equals("picture")) { raimund@251: String name = symbolElement.getAttribute("name"); raimund@251: symbolObj sym = symbolSet.getSymbolByName(name); rrenkert@97: writeHatch(sym); rrenkert@97: } raimund@251: else if(symType.equals("picture")) { raimund@251: // Create the path to the picture. raimund@251: int exists = symbolExists(symbolElement); raimund@251: String n = symbolElement.getAttribute("name"); raimund@251: symbolElement.setAttribute( raimund@251: "name", raimund@251: n + symbolSet.getNumsymbols() + 1); raimund@251: String path = this.map.getMappath(); raimund@251: if (path.endsWith(".map")) { raimund@251: path = path.substring(0, path.lastIndexOf(File.separator) + 1); raimund@251: path += symbolElement.getAttribute("name") + ".png"; raimund@251: } raimund@251: else { raimund@251: path += symbolElement.getAttribute("name") + ".png"; raimund@251: } raimund@251: // Create a new symbol using the path as symbol name. raimund@251: // This is a workarround to set the IMAGE attribute to the path raimund@251: // since this attribute is immutable via mapscript. raimund@251: symbolObj sym = symbolSet.getSymbolByName(path); raimund@251: writePicture(sym, symbolElement, path); raimund@251: } rrenkert@87: else { rrenkert@87: return false; rrenkert@87: } rrenkert@90: rrenkert@120: try { rrenkert@120: saveSymbolSet(symbolSet); rrenkert@120: } rrenkert@120: catch(Exception e) { rrenkert@120: logger.error("Error saving symbol set."); rrenkert@120: return false; rrenkert@120: } rrenkert@90: rrenkert@87: return true; rrenkert@87: } rrenkert@87: rrenkert@181: /** rrenkert@181: * Create a simple point symbol. rrenkert@181: * rrenkert@181: * @param symbol The symbol object. rrenkert@181: */ rrenkert@87: private void writeSimple(symbolObj symbol) { rrenkert@87: logger.debug("writeSimple(symbolObj)"); rrenkert@87: lineObj points = new lineObj(); rrenkert@87: points.add(new pointObj(1,1,0)); rrenkert@87: symbol.setType(MS_SYMBOL_TYPE.MS_SYMBOL_ELLIPSE.swigValue()); rrenkert@87: symbol.setPoints(points); rrenkert@87: symbol.setFilled(1); rrenkert@87: } rrenkert@87: rrenkert@181: /** rrenkert@181: * Create an arrow symbol. rrenkert@181: * rrenkert@181: * @param symbol The symbol object. rrenkert@181: * @param symbolElement DOM element containing symbol attributes. rrenkert@181: */ rrenkert@87: private void writeArrow(symbolObj symbol, Element symbolElement) { rrenkert@87: logger.debug("writeArrow(symbolObj, Element)"); rrenkert@145: double len = 0; rrenkert@145: double width = 1; rrenkert@145: try { rrenkert@145: len = rrenkert@145: Double.parseDouble(symbolElement.getAttribute("length")); rrenkert@145: width = rrenkert@145: Double.parseDouble(symbolElement.getAttribute("width")); rrenkert@145: } rrenkert@145: catch(NumberFormatException nfe) { rrenkert@145: logger.warn ("Error setting arrow symbol."); rrenkert@145: return; rrenkert@145: } rrenkert@87: double ratio = len/width; rrenkert@87: lineObj points = new lineObj(); rrenkert@87: rrenkert@87: points.add(new pointObj(0, 0, 0)); rrenkert@87: points.add(new pointObj((1*ratio), 0.5, 0)); rrenkert@87: points.add(new pointObj(0, 1, 0)); rrenkert@87: points.add(new pointObj(0, 0, 0)); rrenkert@87: symbol.setType(MS_SYMBOL_TYPE.MS_SYMBOL_VECTOR.swigValue()); rrenkert@87: symbol.setPoints(points); rrenkert@87: symbol.setFilled(1); rrenkert@87: } rrenkert@87: rrenkert@181: /** rrenkert@181: * Create a character marker symbol. rrenkert@181: * rrenkert@181: * @param symbol The symbol object. rrenkert@181: * @param symbolElement DOM element containing symbol attributes. rrenkert@181: */ rrenkert@87: private void writeCharacter(symbolObj symbol, Element symbolElement) { rrenkert@87: logger.debug("writeCharacter(symbolObj, Element)"); rrenkert@93: String font = symbolElement.getAttribute("font"); rrenkert@93: //Remove all blank character to match the mapserver fonts.txt. rrenkert@93: font = font.replaceAll(" ", ""); raimund@192: fontSetObj fso = this.map.getFontset(); raimund@192: hashTableObj fonts = fso.getFonts(); raimund@192: String mapFont = fonts.get(font,""); raimund@192: if (mapFont == null || mapFont.equals("")) { raimund@192: logger.warn( raimund@192: "Could not find font " + font + " in font set. " + raimund@192: "Setting FreeSans as symbol font." + raimund@192: "Please add " + font + " to fonts.txt and replace the alias " + raimund@192: "in symbol \"" + symbolElement.getAttribute("name") + "\"."); raimund@192: font = "FreeSans"; raimund@192: } vc11884admin@141: symbol.setName(symbolElement.getAttribute("name")); rrenkert@93: symbol.setFont(font); rrenkert@87: symbol.setType(MS_SYMBOL_TYPE.MS_SYMBOL_TRUETYPE.swigValue()); rrenkert@87: symbol.setAntialias(1); rrenkert@93: symbol.setCharacter("&#" + symbolElement.getAttribute("char") + ";"); rrenkert@87: } rrenkert@87: raimund@251: rrenkert@181: /** raimund@251: * Create the image and a pixmap symbol. raimund@251: * raimund@251: * @param sym The symbol object. raimund@251: * @param symElem The DOM element containing symbol informations. raimund@251: */ raimund@251: private void writePicture(symbolObj sym, Element symElem, String path) { raimund@254: logger.debug("writePicture(symbolObj, Element)"); raimund@251: try { raimund@251: // Create the image as png. raimund@251: if(symElem.hasAttribute("picture")) { raimund@251: Base64 decoder = new Base64(); raimund@251: // Decode the base64 string. raimund@251: byte[] ba = decoder.decode(symElem.getAttribute("picture")); raimund@251: ByteArrayInputStream ins = new ByteArrayInputStream(ba); raimund@251: // Create temporary image from byte array. raimund@251: BufferedImage bi = ImageIO.read(ins); raimund@251: Color c = Color.decode( raimund@251: symElem.getAttribute("transparent_color")); raimund@251: // Make a color transparent. raimund@251: Image im = colorToTransparent(bi, c); raimund@251: raimund@251: // Save image as png. raimund@251: BufferedImage dest = new BufferedImage( raimund@251: bi.getWidth(), raimund@251: bi.getHeight(), raimund@251: BufferedImage.TYPE_INT_ARGB); raimund@251: Graphics2D g2 = dest.createGraphics(); raimund@251: g2.drawImage(im, 0, 0, null); raimund@251: g2.dispose(); raimund@251: File f = new File(path); raimund@251: ImageIO.write(dest, "PNG", f); raimund@251: } raimund@251: // Set symbol attributes. raimund@251: // Overwrite the symbol name conating the path to the image raimund@251: // with the real name. This is part of the workarround(line 137). raimund@251: sym.setName(symElem.getAttribute("name")); raimund@251: sym.setType(MS_SYMBOL_TYPE.MS_SYMBOL_PIXMAP.swigValue()); raimund@251: } raimund@251: catch(Exception e) { raimund@251: logger.warn("Could not save image for pixmap symbol."); raimund@251: } raimund@251: } raimund@251: raimund@251: raimund@251: /** rrenkert@181: * Create a hatch symbol for polygon fill. rrenkert@181: * rrenkert@181: * @param symbol The symbol object. rrenkert@181: */ rrenkert@97: private void writeHatch(symbolObj symbol) { rrenkert@97: logger.debug("writeHatch(symbolObj)"); rrenkert@97: symbol.setType(MS_SYMBOL_TYPE.MS_SYMBOL_HATCH.swigValue()); rrenkert@97: } rrenkert@97: rrenkert@181: /** rrenkert@181: * Determine whether this writer can create the symbol. rrenkert@181: * rrenkert@181: * @param type The symbol type. rrenkert@181: */ rrenkert@87: public boolean canWrite(String type) { rrenkert@87: if (type.equals("point") || rrenkert@87: type.equals("arrow") || rrenkert@97: type.equals("char") || raimund@251: type.equals("line") || raimund@251: type.equals("picture")) { rrenkert@87: return true; rrenkert@87: } rrenkert@87: else { rrenkert@87: return false; rrenkert@87: } rrenkert@87: } rrenkert@90: rrenkert@181: /** rrenkert@181: * Save the symbol set. rrenkert@181: * rrenkert@181: * @param symbols The symbol set object. rrenkert@181: */ raimund@184: public void saveSymbolSet(symbolSetObj symbols) rrenkert@90: throws Exception { rrenkert@93: String path = this.map.getMappath(); raimund@184: String filename = path.substring(path.lastIndexOf(File.separator) + 1, raimund@184: path.lastIndexOf(".")); raimund@184: String symbolPath = filename + "-symbols.sym"; rrenkert@90: if(path.equals("")) { rrenkert@90: return; rrenkert@90: } raimund@174: raimund@174: if (path.endsWith(".map")) { raimund@174: path = path.substring(0, path.lastIndexOf(File.separator) + 1); raimund@175: path += symbolPath; raimund@174: } rrenkert@93: else { raimund@175: path += symbolPath; rrenkert@90: } raimund@175: path = path.replaceAll("\\\\", "/"); rrenkert@90: symbols.save(path); raimund@184: this.map.setSymbolSet(path); rrenkert@90: } vc11884admin@141: rrenkert@181: /** rrenkert@181: * Determine whether the symbol exists. rrenkert@181: * rrenkert@181: * @param elem DOM element containing the symbol attributes. rrenkert@181: * rrenkert@181: * @return Returns the symbol index if the symbol exists, else returns -1. rrenkert@181: */ vc11884admin@141: private int symbolExists (Element elem) { vc11884admin@141: symbolSetObj symbolSet = map.getSymbolset(); vc11884admin@141: for (int i = 0; i < symbolSet.getNumsymbols(); i++) { vc11884admin@141: symbolObj sym = symbolSet.getSymbol(i); vc11884admin@141: int stype = sym.getType(); vc11884admin@141: String etype = elem.getAttribute("style"); vc11884admin@141: if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_ELLIPSE.swigValue () && vc11884admin@141: etype.equals("point")) { rrenkert@145: return i; vc11884admin@141: } vc11884admin@141: else if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_VECTOR.swigValue () && vc11884admin@141: etype.equals("arrow")) { rrenkert@145: return i; vc11884admin@141: } vc11884admin@141: else if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_TRUETYPE.swigValue () && vc11884admin@141: etype.equals("char")) { vc11884admin@141: String font = elem.getAttribute("font"); vc11884admin@141: font = font.replaceAll(" ", ""); vc11884admin@141: String c = elem.getAttribute("char"); vc11884admin@141: c = "&#" + c + ";"; vc11884admin@141: if (font.equals(sym.getFont()) && vc11884admin@141: c.equals(sym.getCharacter())) { vc11884admin@141: return i; vc11884admin@141: } vc11884admin@141: vc11884admin@141: } vc11884admin@141: else if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_HATCH.swigValue () && vc11884admin@141: etype.equals("hatch")) { rrenkert@145: return i; vc11884admin@141: } raimund@251: else if(stype == MS_SYMBOL_TYPE.MS_SYMBOL_PIXMAP.swigValue() && raimund@251: etype.equals("picture")) { raimund@251: return i; raimund@251: } vc11884admin@141: } vc11884admin@141: return -1; vc11884admin@141: } raimund@251: raimund@251: raimund@251: /** raimund@251: * Transform a color to transparency. raimund@251: * raimund@251: * @param bi The image. raimund@251: * @param col the color. raimund@251: * raimund@251: * @return The image with transparent color. raimund@251: */ raimund@251: private Image colorToTransparent(BufferedImage bi, Color col) { raimund@251: final int markerRGB = col.getRGB() | 0xFF000000; raimund@251: ImageFilter filter = new RGBImageFilter() { raimund@251: public final int filterRGB(int x, int y, int rgb) { raimund@251: if ((rgb | 0xFF000000) == markerRGB) { raimund@251: // Mark the alpha bits as zero - transparent raimund@251: return 0x00FFFFFF & rgb; raimund@251: } raimund@251: else { raimund@251: // nothing to do raimund@251: return rgb; raimund@251: } raimund@251: } raimund@251: }; raimund@251: ImageProducer ip = new FilteredImageSource(bi.getSource(), filter); raimund@251: return Toolkit.getDefaultToolkit().createImage(ip); raimund@251: } raimund@251: rrenkert@87: } rrenkert@87: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :