//$$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/tools/shape/AVL2SLD.java,v 1.18 2006/11/22 12:13:20 poth Exp $$ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de 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: Andreas Poth lat/lon GmbH Aennchenstraße 19 53177 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.tools.shape; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.deegree.framework.util.ImageUtils; import org.deegree.framework.util.StringTools; import org.deegree.framework.xml.Marshallable; import org.deegree.graphics.sld.AbstractLayer; import org.deegree.graphics.sld.AbstractStyle; import org.deegree.graphics.sld.ExternalGraphic; import org.deegree.graphics.sld.FeatureTypeStyle; import org.deegree.graphics.sld.Fill; import org.deegree.graphics.sld.Font; import org.deegree.graphics.sld.Graphic; import org.deegree.graphics.sld.GraphicFill; import org.deegree.graphics.sld.LabelPlacement; import org.deegree.graphics.sld.LineSymbolizer; import org.deegree.graphics.sld.Mark; import org.deegree.graphics.sld.NamedLayer; import org.deegree.graphics.sld.PointPlacement; import org.deegree.graphics.sld.PointSymbolizer; import org.deegree.graphics.sld.PolygonSymbolizer; import org.deegree.graphics.sld.Rule; import org.deegree.graphics.sld.Stroke; import org.deegree.graphics.sld.StyleFactory; import org.deegree.graphics.sld.StyledLayerDescriptor; import org.deegree.graphics.sld.Symbolizer; import org.deegree.graphics.sld.TextSymbolizer; import org.deegree.io.shpapi.MainFile; import org.deegree.io.shpapi.ShapeConst; import org.deegree.model.filterencoding.ComplexFilter; import org.deegree.model.filterencoding.Filter; import org.deegree.model.filterencoding.FilterConstructionException; import org.deegree.model.filterencoding.Literal; import org.deegree.model.filterencoding.LogicalOperation; import org.deegree.model.filterencoding.Operation; import org.deegree.model.filterencoding.OperationDefines; import org.deegree.model.filterencoding.PropertyIsCOMPOperation; import org.deegree.model.filterencoding.PropertyName; /** * <p> * This class converts ESRI *.avl files to OGC SLD documents. The current * version of this tool isn't able to convert each and every construction that * is possible with an *.avl file. But most of the common expressions will be * mapped. * </p> * <p> * Because SLD (version 1.0.0) does not know inline bitmap or fill pattern * definition directly (maybe it is possible using some SVG tags) all polygon * fill patterns must be converted to images that are written to the file system * and referenced as external graphic by the created SLD style. The similar is * true for symbol definitions. SLD just 'knowns' a few predefined symbols that * are not able to capture all symbols known by ArcView. In this context deegree * also will extend the well known symbol by using ASCII codes to references * symbols defined in an available font. This will enable deegree to transform * the ArcView option defining a symbol by using an ACSII character directly. At * least even if the synthax for this is SLD compliant most SLD implementation * probably won't be able to evaluate this. * </p> * * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a> * @author last edited by: $Author: poth $ * * @version 1.1, $Revision: 1.18 $, $Date: 2006/11/22 12:13:20 $ * * @since 1.1 */ public class AVL2SLD { private String fileRootName = null; private String targetDir = null; private Map blocks = new HashMap(); private static AVLPointSymbolCodeList cl = null; public AVL2SLD( String fileRootName, String targetDir ) { this.fileRootName = fileRootName; this.targetDir = targetDir; if ( !this.targetDir.endsWith( "/" ) ) { this.targetDir = this.targetDir + "/"; } } /** * reads the avl file assigned to a class instance * * @throws IOException */ public void read() throws IOException { Reader reader = new FileReader( fileRootName + ".avl" ); StringBuffer sb = new StringBuffer( 50000 ); int c = 0; while ( ( c = reader.read() ) > -1 ) { if ( c == 9 ) c = ' '; // if ( c != 10 && c != 13) { sb.append( (char) c ); // } } reader.close(); // create a entry in 'blocks' for each '(' ... ')' // enclosed section String[] s1 = splitavl( sb.toString() ); for ( int i = 1; i < s1.length; i++ ) { // write each KVP of section to a map and store it // in the blocks map. // the Class, Pattern and he Child key will be treated // as array because it isn't unique int pos = s1[i].indexOf( ' ' ); if ( pos < 0 ) continue; String section = s1[i].substring( 0, pos ).trim(); String[] s2 = StringTools.toArray( s1[i].substring( pos, s1[i].length() ), ":\n", false ); Map block = new HashMap(); for ( int j = 0; j < s2.length; j = j + 2 ) { if ( s2[j].trim().equals( "Class" ) ) { List list = (List) block.get( "Class" ); if ( list == null ) { list = new ArrayList(); } list.add( s2[j + 1] ); block.put( s2[j], list ); } else if ( s2[j].trim().equals( "Child" ) ) { List list = (List) block.get( "Child" ); if ( list == null ) { list = new ArrayList(); } list.add( s2[j + 1] ); block.put( s2[j], list ); } else if ( s2[j].trim().equals( "Pattern" ) ) { List list = (List) block.get( "Pattern" ); if ( list == null ) { list = new ArrayList(); } list.add( s2[j + 1] ); block.put( s2[j], list ); } else if ( s2[j].trim().equals( "Bits" ) ) { List list = (List) block.get( "Bits" ); if ( list == null ) { list = new ArrayList(); } list.add( s2[j + 1] ); block.put( s2[j], list ); } else { block.put( s2[j], s2[j + 1].trim() ); } } blocks.put( section, block ); } } /** * returns a <tt>Style</tt>. For each class/child contained in a avl file * one <tt>Rule</tt> will be created * * @return * @throws IOException * @throws Exception */ public AbstractStyle getStyle() throws IOException, Exception { Map odb = (Map) blocks.get( "ODB.1" ); String roots = (String) odb.get( "Roots" ); Map legend = (Map) blocks.get( "Legend." + roots ); String filterCol = null; if ( legend.get( "FieldNames" ) != null ) { Map block = (Map) blocks.get( "AVStr." + legend.get( "FieldNames" ) ); filterCol = (String) block.get( "S" ); filterCol = StringTools.validateString( filterCol, "\"" ).toUpperCase(); } int geoType = getGeometryType(); AbstractStyle style = null; switch ( geoType ) { case ShapeConst.SHAPE_TYPE_POINT: style = createPointStyle( legend, filterCol ); break; case ShapeConst.SHAPE_TYPE_POLYLINE: style = createLinesStyle( legend, filterCol ); break; case ShapeConst.SHAPE_TYPE_POLYGON: style = createPolygonStyle( legend, filterCol ); break; case ShapeConst.SHAPE_TYPE_MULTIPOINT: style = createPointStyle( legend, filterCol ); break; default: throw new Exception( "unknown geometry type: " + geoType ); } return style; } /** * creates a <tt>StyledLayerDescriptor</tt> from the avl file assigned to * the instace of a <tt>AVLReader</tt>. The returned instance of a * <tt>StyledLayerDescriptor</tt> just contains one style that may * containes several <tt>Rule</tt>s * * @return * @throws IOException * @throws Exception */ public StyledLayerDescriptor getStyledLayerDescriptor() throws IOException, Exception { AbstractStyle style = getStyle(); String[] t = StringTools.toArray( fileRootName, "/", false ); String name = "default:" + t[t.length - 1]; AbstractLayer layer = new NamedLayer( name, null, new AbstractStyle[] { style } ); return new StyledLayerDescriptor( new AbstractLayer[] { layer }, "1.0.0" ); } /** * parse a string and return array of blocks between braces "(" and ")". * It accounts for braces in quoted strings. * * @param s * string to parse */ public static String[] splitavl( String s ) { if ( s == null || s.equals( "" ) ) { return new String[0]; } Pattern pat = Pattern.compile( "\\(([^)\"]|\"[^\"]*\")*\\)" ); Matcher mat = pat.matcher( s ); int prevend = 0; ArrayList<String> vec = new ArrayList<String>(); while ( mat.find() ) { int start = mat.start(); int end = mat.end(); if ( prevend < start - 1 ) { String str = s.substring( prevend, start ).trim(); if ( str.length() > 0 ) { vec.add( str ); } } String str = s.substring( start + 1, end - 1 ).trim(); if ( str.length() > 0 ) { vec.add( str ); } prevend = end; } if ( prevend < s.length() - 1 ) { String str = s.substring( prevend ).trim(); if ( str.length() > 0 ) { vec.add( str ); } } // no value selected if ( vec.size() == 0 ) { return new String[0]; } return vec.toArray( new String[vec.size()] ); } private int getGeometryType() throws IOException { MainFile mf = new MainFile( fileRootName ); int type = mf.getShapeTypeByRecNo( 1 ); mf.close(); return type; } /** * creates a <tt>Style</tt>. For each class/child contained in a avl file * one <tt>Rule</tt> will be created * * @param legend * @param filterCol * @return */ private AbstractStyle[] createPointStyles( Map legend, String filterCol ) { AbstractStyle[] styles = null; return styles; } /** * creates a <tt>Style</tt>. For each class/child contained in a avl file * one <tt>Rule</tt> will be created * * @param legend * @param filterCol * @return */ private AbstractStyle createPointStyle( Map legend, String filterCol ) throws FilterConstructionException { try { cl = new AVLPointSymbolCodeList(); } catch ( Exception e ) { e.printStackTrace(); } String tmp = (String) legend.get( "Symbols" ); Map block = (Map) blocks.get( "SymList." + tmp ); List classes = (List) legend.get( "Class" ); List children = (List) block.get( "Child" ); List list = new ArrayList( classes.size() ); for ( int i = 0; i < classes.size(); i++ ) { String clNo = (String) classes.get( i ); Map clss = (Map) blocks.get( "LClass." + clNo ); String childNo = (String) children.get( i ); Map child = (Map) blocks.get( "CMkSym." + childNo ); Rule rule = null; if ( child == null ) { child = (Map) blocks.remove( "BMkSym." + childNo ); rule = createSimplePointRule( clss, child, filterCol ); } else { rule = createComplexPointRule( clss, child, filterCol ); } if ( rule != null ) { list.add( rule ); } } Rule[] rules = (Rule[]) list.toArray( new Rule[list.size()] ); FeatureTypeStyle fts = StyleFactory.createFeatureTypeStyle( rules ); String[] t = StringTools.toArray( fileRootName, "/", false ); String name = "default:" + t[t.length - 1]; return StyleFactory.createStyle( name, null, null, fts ); } /** * creates a Style for a line symbol * * @param clss * @param child * @return */ private Rule createSimplePointRule( Map clss, Map child, String filterCol ) throws FilterConstructionException { if ( clss.get( "IsNoData" ) != null ) { return null; } String label = (String) clss.get( "Label" ); label = StringTools.validateString( label, "\"" ); Filter filter = createFilter( clss, filterCol ); // get foreground color String clrIdx = (String) child.get( "Color" ); Map color = (Map) blocks.get( "TClr." + clrIdx ); double fgOpacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { fgOpacity = 0f; } Color fgColor = createColor( color ); if ( fgColor == null ) { fgColor = Color.BLACK; } // get background color (what ever this means) clrIdx = (String) child.get( "BgColor" ); color = (Map) blocks.get( "TClr." + clrIdx ); double bgOpacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { bgOpacity = 0f; } Color bgColor = createColor( color ); if ( bgColor == null ) { bgColor = Color.BLACK; } double size = Double.parseDouble( (String) child.get( "Size" ) ); double rotation = Double.parseDouble( (String) child.get( "Angle" ) ); // creation of a font from a avl file is a triky thing because // esri uses their own font names and codes // --> don' know if this works String fntIdx = (String) child.get( "Font" ); Map fntMap = (Map) blocks.get( "NFont." + fntIdx ); Font font = createFont( fntMap ); PointPlacement pp = StyleFactory.createPointPlacement(); LabelPlacement labelPlacement = StyleFactory.createLabelPlacement( pp ); TextSymbolizer textSym = StyleFactory.createTextSymbolizer( fgColor, font, filterCol, labelPlacement ); String patternIdx = (String) ( (ArrayList) child.get( "Pattern" ) ).get( 0 ); String symbol = cl.getSymbol( patternIdx ); // create a Mark with a stroke and fill to controll both // opacities Stroke stroke = StyleFactory.createStroke( fgColor, 1, fgOpacity, null, "mitre", "butt" ); Fill fill = StyleFactory.createFill( bgColor, bgOpacity ); Mark mark = StyleFactory.createMark( symbol, fill, stroke ); Graphic graphic = StyleFactory.createGraphic( null, mark, 1, size, rotation ); PointSymbolizer pointSym = StyleFactory.createPointSymbolizer( graphic ); return StyleFactory.createRule( new Symbolizer[] { pointSym, textSym }, label, label, "", null, filter, false, 0, 9E99 ); } /** * * @param clss * @param child * @param filterCol * @return * @throws FilterConstructionException */ private Rule createComplexPointRule( Map clss, Map child, String filterCol ) throws FilterConstructionException { if ( clss.get( "IsNoData" ) != null ) { return null; } String tmp = (String) child.get( "Symbols" ); Map block = (Map) blocks.get( "SymList." + tmp ); List children = (List) block.get( "Child" ); List smbls = new ArrayList(); for ( int i = 0; i < children.size(); i++ ) { String childNo = (String) children.get( i ); Map child_ = (Map) blocks.get( "CMkSym." + childNo ); Rule rule = null; if ( child_ == null ) { child = (Map) blocks.remove( "BMkSym." + childNo ); rule = createSimplePointRule( clss, child, filterCol ); } else { rule = createComplexPointRule( clss, child, filterCol ); } Symbolizer[] sym = rule.getSymbolizers(); for ( int j = 0; j < sym.length; j++ ) { smbls.add( sym[j] ); } } Symbolizer[] sym = new Symbolizer[smbls.size()]; sym = (Symbolizer[]) smbls.toArray( sym ); String label = (String) clss.get( "Label" ); label = StringTools.validateString( label, "\"" ); Filter filter = createFilter( clss, filterCol ); return StyleFactory.createRule( sym, label, label, "", null, filter, false, 0, 9E99 ); } private Font createFont( Map fntMap ) { String idx = (String) fntMap.get( "Family" ); String family = (String) ( (Map) blocks.get( "AVStr." + idx ) ).get( "S" ); idx = (String) fntMap.get( "Name" ); String name = (String) ( (Map) blocks.get( "AVStr." + idx ) ).get( "S" ); idx = (String) fntMap.get( "Style" ); String style = (String) ( (Map) blocks.get( "AVStr." + idx ) ).get( "S" ); String weight = (String) fntMap.get( "Weight" ); String wideness = (String) fntMap.get( "Wideness" ); boolean italic = style.equals( "Italic" ); boolean bold = Integer.parseInt( weight ) > 1; return StyleFactory.createFont( family, italic, bold, 12 ); } /** * creates a <tt>Style</tt>. For each class/child contained in a avl file * one <tt>Rule</tt> will be created * * @param legend * @param filterCol * @return */ private AbstractStyle createLinesStyle( Map legend, String filterCol ) throws FilterConstructionException { String tmp = (String) legend.get( "Symbols" ); Map block = (Map) blocks.get( "SymList." + tmp ); List classes = (List) legend.get( "Class" ); List children = (List) block.get( "Child" ); List list = new ArrayList( classes.size() ); for ( int i = 0; i < classes.size(); i++ ) { String clNo = (String) classes.get( i ); Map clss = (Map) blocks.get( "LClass." + clNo ); String childNo = (String) children.get( i ); Map child = (Map) blocks.get( "BLnSym." + childNo ); if ( child == null ) { // won't be treated correctly because we can't transform // lines with sambols at the moment child = (Map) blocks.get( "CLnSym." + childNo ); } Rule rule = createLineRule( clss, child, filterCol ); if ( rule != null ) { list.add( rule ); } } Rule[] rules = (Rule[]) list.toArray( new Rule[list.size()] ); FeatureTypeStyle fts = StyleFactory.createFeatureTypeStyle( rules ); String[] t = StringTools.toArray( fileRootName, "/", false ); String name = "default:" + t[t.length - 1]; return StyleFactory.createStyle( name, null, null, fts ); } /** * creates a Style for a line symbol * * @param clss * @param child * @return */ private Rule createLineRule( Map clss, Map child, String filterCol ) throws FilterConstructionException { if ( clss.get( "IsNoData" ) != null ) { return null; } String label = (String) clss.get( "Label" ); label = StringTools.validateString( label, "\"" ); Filter filter = createFilter( clss, filterCol ); String clrIdx = (String) child.get( "Color" ); Map color = (Map) blocks.get( "TClr." + clrIdx ); double opacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) && color.size() > 0 ) { opacity = 0f; } Color colour = createColor( color ); if ( colour == null ) { colour = Color.BLACK; } double width = 1.0; // default Width if ( child.get( "Width" ) != null ) { width = Double.parseDouble( (String) child.get( "Width" ) ) + width; } // double width = Double.parseDouble( (String)child.get("Width") ) + 1; List pl = (List) child.get( "Pattern" ); if ( child.get( "Pattern" ) == null ) { // create a default pattern List if // it is null pl = new ArrayList(); for ( int i = 0; i < 16; i++ ) { // Fill the default List with // default values "0.00000000000000" pl.add( "0.00000000000000" ); } } // List pl = (List)child.get("Pattern"); float[] dashArray = createDashArray( pl ); Stroke stroke = StyleFactory.createStroke( colour, width, opacity, dashArray, "mitre", "butt" ); LineSymbolizer lineSym = StyleFactory.createLineSymbolizer( stroke ); return StyleFactory.createRule( new Symbolizer[] { lineSym }, label, label, "", null, filter, false, 0, 9E99 ); } /** * creates a <tt>Style</tt>. For each class/child contained in a avl file * one <tt>Rule</tt> will be created * * @param legend * @param filterCol * @return */ private AbstractStyle createPolygonStyle( Map legend, String filterCol ) throws Exception { String tmp = (String) legend.get( "Symbols" ); Map block = (Map) blocks.get( "SymList." + tmp ); List classes = (List) legend.get( "Class" ); List children = (List) block.get( "Child" ); List list = new ArrayList( classes.size() ); for ( int i = 0; i < classes.size(); i++ ) { String clNo = (String) classes.get( i ); Map clss = (Map) blocks.get( "LClass." + clNo ); String childNo = (String) children.get( i ); Map child = (Map) blocks.get( "BShSym." + childNo ); Rule rule = null; if ( child == null ) { // VShSym is a vector polygon fill child = (Map) blocks.get( "VShSym." + childNo ); rule = createPolygonVecRule( clss, child, filterCol ); } else { rule = createPolygonBMPRule( clss, child, filterCol ); } if ( rule != null ) { list.add( rule ); } // TODO // write special method for vector polygon fill } Rule[] rules = (Rule[]) list.toArray( new Rule[list.size()] ); FeatureTypeStyle fts = StyleFactory.createFeatureTypeStyle( rules ); String[] t = StringTools.toArray( fileRootName, "/", false ); String name = "default:" + t[t.length - 1]; return StyleFactory.createStyle( name, null, null, fts ); } /** * creates a Style for a line symbol * * @param clss * @param child * @return */ private Rule createPolygonBMPRule( Map clss, Map child, String filterCol ) throws Exception { if ( clss.get( "IsNoData" ) != null ) { return null; } String label = (String) clss.get( "Label" ); label = StringTools.validateString( label, "\"" ); Filter filter = null; if ( filterCol != null ) { filter = createFilter( clss, filterCol ); } // get foreground color String clrIdx = (String) child.get( "Color" ); Map color = (Map) blocks.get( "TClr." + clrIdx ); double opacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { opacity = 0f; } Color fgColor = createColor( color ); if ( fgColor == null ) { fgColor = Color.BLACK; } // get color of the outlining stroke clrIdx = (String) child.get( "OutlineColor" ); color = (Map) blocks.get( "TClr." + clrIdx ); double outLOpacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { outLOpacity = 0f; } Color outLColor = createColor( color ); if ( outLColor == null ) { outLColor = Color.BLACK; } // get background color clrIdx = (String) child.get( "BgColor" ); color = (Map) blocks.get( "TClr." + clrIdx ); double bgOpacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { bgOpacity = 0f; } Color bgColor = createColor( color ); // create fill pattern as an image that will be referenced as // external graphic String stippleIdx = (String) child.get( "Stipple" ); String src = null; if ( stippleIdx != null ) { Map stipple = (Map) blocks.get( "Stipple." + stippleIdx ); src = createExternalGraphicFromStipple( stipple, label, fgColor, bgColor ); } double width = Double.parseDouble( (String) child.get( "OutlineWidth" ) ) + 1; Stroke stroke = StyleFactory.createStroke( outLColor, width, outLOpacity, null, "mitre", "butt" ); Fill fill = null; if ( stippleIdx != null ) { ExternalGraphic eg = StyleFactory.createExternalGraphic( "file:///" + src, "image/gif" ); Graphic graph = StyleFactory.createGraphic( eg, null, opacity, 10, 0 ); GraphicFill gf = StyleFactory.createGraphicFill( graph ); fill = StyleFactory.createFill( fgColor, opacity, gf ); } else { fill = StyleFactory.createFill( fgColor, opacity ); } PolygonSymbolizer polySym = StyleFactory.createPolygonSymbolizer( stroke, fill ); return StyleFactory.createRule( new Symbolizer[] { polySym }, label, label, "", null, filter, false, 0, 9E99 ); } /** * creates a Style for a line symbol * * @param clss * @param child * @return */ private Rule createPolygonVecRule( Map clss, Map child, String filterCol ) throws Exception { if ( clss.get( "IsNoData" ) != null ) { return null; } String label = (String) clss.get( "Label" ); label = StringTools.validateString( label, "\"" ); Filter filter = null; if ( filterCol != null ) { filter = createFilter( clss, filterCol ); } // get foreground color String clrIdx = (String) child.get( "Color" ); Map color = (Map) blocks.get( "TClr." + clrIdx ); double opacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { opacity = 0f; } Color fgColor = createColor( color ); if ( fgColor == null ) { fgColor = Color.BLACK; } // get color of the outlining stroke clrIdx = (String) child.get( "OutlineColor" ); color = (Map) blocks.get( "TClr." + clrIdx ); double outLOpacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { outLOpacity = 0f; } Color outLColor = createColor( color ); if ( outLColor == null ) { outLColor = Color.BLACK; } // get background color clrIdx = (String) child.get( "BgColor" ); color = (Map) blocks.get( "TClr." + clrIdx ); double bgOpacity = 1f; if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { bgOpacity = 0f; } Color bgColor = createColor( color ); // create fill pattern as an image that will be referenced as // external graphic String stippleIdx = (String) child.get( "Stipple" ); String src = null; if ( stippleIdx != null ) { Map stipple = (Map) blocks.get( "Stipple." + stippleIdx ); src = createExternalGraphicFromStipple( stipple, label, fgColor, bgColor ); } double width = Double.parseDouble( (String) child.get( "OutlineWidth" ) ) + 1; Stroke stroke = StyleFactory.createStroke( outLColor, width, outLOpacity, null, "mitre", "butt" ); Fill fill = null; if ( stippleIdx != null ) { ExternalGraphic eg = StyleFactory.createExternalGraphic( "file:///" + src, "image/gif" ); Graphic graph = StyleFactory.createGraphic( eg, null, opacity, 10, 0 ); GraphicFill gf = StyleFactory.createGraphicFill( graph ); fill = StyleFactory.createFill( fgColor, opacity, gf ); } else { fill = StyleFactory.createFill( fgColor, opacity ); } PolygonSymbolizer polySym = StyleFactory.createPolygonSymbolizer( stroke, fill ); return StyleFactory.createRule( new Symbolizer[] { polySym }, label, label, "", null, filter, false, 0, 9E99 ); } /** * creates an image from a stipple and stores it as a gif image. The method * returns the full name of the stored image. * * @param stipple * @return */ private String createExternalGraphicFromStipple( Map stipple, String label, Color fg, Color bg ) throws Exception { label = label.replace( ' ', '_' ); label = label.replace( '.', '_' ); label = label.replace( ';', '_' ); label = label.replace( ',', '_' ); label = label.replace( '-', '_' ); label = label.replace( ':', '_' ); String tmp = (String) stipple.get( "Columns" ); int cols = Integer.parseInt( tmp ); tmp = (String) stipple.get( "Rows" ); int rows = Integer.parseInt( tmp ); List bList = (List) stipple.get( "Bits" ); StringBuffer bStr = new StringBuffer( 1000 ); for ( int i = 0; i < bList.size(); i++ ) { String[] t = StringTools.toArray( ( (String) bList.get( i ) ).trim(), " ", false ); for ( int j = 0; j < t.length; j++ ) { bStr.append( t[j] ); } } char[] ch = bStr.toString().toCharArray(); BufferedImage bi = createFillPattern( cols, rows, ch, fg, bg ); final String format = "gif"; String[] t = StringTools.toArray( fileRootName, "/", false ); String base = t[t.length - 1]; FileOutputStream fos = new FileOutputStream( targetDir + base + '_' + label + "." + format ); ImageUtils.saveImage( bi, fos, format, 1.0f ); // Encoders.encodeGif(fos,bi); fos.close(); if ( targetDir.startsWith( "/" ) ) { return targetDir.substring( 1, targetDir.length() ) + base + '_' + label + "." + format; } return targetDir + base + '_' + label + "." + format; } /** * creates a <tt>BufferedImage</tt> using the stipple contained in the * passed char array. * * @param cols * @param rows * @param ch * @return */ private BufferedImage createFillPattern( int cols, int rows, char[] ch, Color fg, Color bg ) { BufferedImage bi = new BufferedImage( cols, rows, BufferedImage.TYPE_INT_ARGB ); int cntChar = 0; byte[] bTmp = null; char chr = ' '; int hexCnt = 0; if ( cols % 8 != 0 ) { hexCnt = ( cols / 8 + 1 ) * 8; } else { hexCnt = cols; } for ( int i = 0; i < rows; i++ ) { for ( int j = 0; j < hexCnt; j++ ) { if ( j % 4 == 0 ) { chr = ch[cntChar++]; bTmp = getBits( chr ); } if ( j < cols ) { if ( bTmp[j % 4] == 0 ) { if ( bg != null ) { bi.setRGB( j, i, bg.getRGB() ); } } else { bi.setRGB( j, i, fg.getRGB() ); } } } } return bi; } private byte[] getBits( int ch ) { switch ( ch ) { case '0': return new byte[] { 0, 0, 0, 0 }; case '1': return new byte[] { 0, 0, 0, 1 }; case '2': return new byte[] { 0, 0, 1, 0 }; case '3': return new byte[] { 0, 0, 1, 1 }; case '4': return new byte[] { 0, 1, 0, 0 }; case '5': return new byte[] { 0, 1, 0, 1 }; case '6': return new byte[] { 0, 1, 1, 0 }; case '7': return new byte[] { 0, 1, 1, 1 }; case '8': return new byte[] { 1, 0, 0, 0 }; case '9': return new byte[] { 1, 0, 0, 1 }; case 'a': return new byte[] { 1, 0, 1, 0 }; case 'b': return new byte[] { 1, 0, 1, 1 }; case 'c': return new byte[] { 1, 1, 0, 0 }; case 'd': return new byte[] { 1, 1, 0, 1 }; case 'e': return new byte[] { 1, 1, 1, 0 }; case 'f': return new byte[] { 1, 1, 1, 1 }; default: return new byte[] { 0, 0, 0, 0 }; } } /** * creates a dasharray for constructing a stroke from the pattern entries of * a avl xxxSym. block * * @param pl * @return */ private float[] createDashArray( List pl ) { int cnt = 0; for ( int i = 0; i < pl.size(); i++ ) { if ( Float.parseFloat( (String) pl.get( i ) ) > 0 ) { cnt++; } else { break; } } float[] pattern = null; if ( cnt > 0 ) { pattern = new float[cnt]; for ( int i = 0; i < pattern.length; i++ ) { pattern[i] = Float.parseFloat( (String) pl.get( i ) ); } } return pattern; } /** * creates a AWT color from a val color block * * @param color * @return */ private Color createColor( Map color ) { StringBuffer hex = new StringBuffer( "0x" ); if ( color != null && !"\"Transparent\"".equals( color.get( "Name" ) ) ) { String red = (String) color.get( "Red" ); if ( red == null ) red = "0x0000"; int c = (int) ( ( Integer.decode( red ).intValue() / 65535f ) * 255 ); if ( c < 15 ) { hex.append( 0 ); } hex.append( Integer.toHexString( c ) ); String green = (String) color.get( "Green" ); if ( green == null ) green = "0x0000"; c = (int) ( ( Integer.decode( green ).intValue() / 65535f ) * 255 ); if ( c < 15 ) { hex.append( 0 ); } hex.append( Integer.toHexString( c ) ); String blue = (String) color.get( "Blue" ); if ( blue == null ) blue = "0x0000"; c = (int) ( ( Integer.decode( blue ).intValue() / 65535f ) * 255 ); if ( c < 15 ) { hex.append( 0 ); } hex.append( Integer.toHexString( c ) ); } else { // hex.append("000000"); return null; } return Color.decode( hex.toString() ); } /** * * @param clss * @param filterCol * @return * @throws FilterConstructionException */ private Filter createFilter( Map clss, String filterCol ) throws FilterConstructionException { if ( clss.get( "Label" ) == null ) { return null; } Filter filter = null; String maxN = (String) clss.get( "MaxStr" ); String minN = (String) clss.get( "MinStr" ); if ( maxN != null && minN != null ) { filter = createStringFilter( clss, filterCol ); } else { filter = createNumberFilter( clss, filterCol ); } return filter; } /** * @param filterCol * @param maxN * @param minN * @return * @throws FilterConstructionException */ private Filter createStringFilter( Map clss, String filterCol ) { Filter filter; Operation oper = null; String maxN = (String) clss.get( "MaxStr" ); String minN = (String) clss.get( "MinStr" ); maxN = maxN.substring( 1, maxN.length() - 1 ); minN = minN.substring( 1, minN.length() - 1 ); if ( maxN.equals( minN ) ) { oper = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, new PropertyName( filterCol ), new Literal( minN ) ); } else { ArrayList list = new ArrayList(); Operation op = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISLESSTHANOREQUALTO, new PropertyName( filterCol ), new Literal( maxN ) ); list.add( op ); op = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISGREATERTHANOREQUALTO, new PropertyName( filterCol ), new Literal( minN ) ); list.add( op ); oper = new LogicalOperation( OperationDefines.AND, list ); } filter = new ComplexFilter( oper ); return filter; } /** * @param clss * @param filterCol * @return * @throws FilterConstructionException */ private Filter createNumberFilter( Map clss, String filterCol ) { String maxN = (String) clss.get( "MaxNum" ); String minN = (String) clss.get( "MinNum" ); if ( maxN == null ) { maxN = "9E99"; } if ( minN == null ) { minN = "-9E99"; } double t1 = Double.parseDouble( minN ); double t2 = Double.parseDouble( maxN ); Operation oper = null; if ( t1 == t2 ) { // if t1 == t2 no range is defined and so an 'is equal to' // opertaion must be used if ( ( (int) t1 ) == t1 ) { // if no significant fraction values are present // cast the value to int oper = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, new PropertyName( filterCol ), new Literal( "" + ( (int) t1 ) ) ); } else { oper = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, new PropertyName( filterCol ), new Literal( "" + t1 ) ); } } else { // if t1 != t2 range of valid values is defined and so an so two // operation (one for each border of the range) are used ArrayList list = new ArrayList(); Operation op = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISLESSTHANOREQUALTO, new PropertyName( filterCol ), new Literal( maxN ) ); list.add( op ); op = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISGREATERTHANOREQUALTO, new PropertyName( filterCol ), new Literal( minN ) ); list.add( op ); oper = new LogicalOperation( OperationDefines.AND, list ); } return new ComplexFilter( oper ); } private static String[] getAVLFiles( String dir ) { File file = new File( dir ); return file.list( new DFileFilter() ); } private static void printHelp() { System.out.println( "Converts ESRI *.avl files to OGC SLD documents. The current version of " ); System.out.println( "this tool isn't able to convert each and every construction that is " ); System.out.println( "possible with an *.avl file. But most of the common expressions will be mapped." ); System.out.println( "Supported are *.avl files for point, lines and polygons." ); System.out.println( "" ); System.out.println( "-sourceFile -> full path to the *.avl file to be converted. " ); System.out.println( " in the same directory a shapefile with the same name as the *.avl" ); System.out.println( " file must exist! (conditional, -sourceFile or -sourceDir must " ); System.out.println( " be defined)" ); System.out.println( "-sourceDir -> directory containing one or more *.avl files. for each existing" ); System.out.println( " *.avl file a shapefile with the same base name must exist. " ); System.out.println( " (conditional, -sourceFile or -sourceDir must be defined)" ); System.out.println( "-targetDir -> directory where the created SLD document(s) will be stored. (optional)" ); System.out.println( "-h -> print this help" ); } /** * * @param args * @throws Exception */ public static void main( String[] args ) throws Exception { Map map = new HashMap(); for ( int i = 0; i < args.length; i += 2 ) { map.put( args[i], args[i + 1] ); } if ( map.get( "-sourceFile" ) == null && map.get( "-sourceDir" ) == null && map.get( "-h" ) == null ) { System.out.println( "-sourceFile or -sourceDir must be defined!" ); System.out.println(); printHelp(); System.exit( 1 ); } if ( map.get( "-h" ) != null ) { printHelp(); System.exit( 0 ); } String targetDir = "."; String[] sourceFiles = null; if ( map.get( "-sourceFile" ) != null ) { sourceFiles = new String[] { (String) map.get( "-sourceFile" ) }; // set the default target directory to the sourceFile's directory targetDir = sourceFiles[0].substring( 0, sourceFiles[0].lastIndexOf( "/" ) ); } if ( sourceFiles == null ) { String sourceDir = (String) map.get( "-sourceDir" ); sourceFiles = getAVLFiles( sourceDir ); for ( int i = 0; i < sourceFiles.length; i++ ) { sourceFiles[i] = sourceDir + '/' + sourceFiles[i]; } // set the default target directory to the source directory targetDir = sourceDir; } // String targetDir = "."; if ( map.get( "-targetDir" ) != null ) { targetDir = (String) map.get( "-targetDir" ); } for ( int i = 0; i < sourceFiles.length; i++ ) { System.out.println( "processing: " + sourceFiles[i] ); int pos = sourceFiles[i].lastIndexOf( '.' ); String file = sourceFiles[i].substring( 0, pos ); AVL2SLD avl = new AVL2SLD( file, targetDir ); avl.read(); StyledLayerDescriptor sld = avl.getStyledLayerDescriptor(); String[] t = StringTools.toArray( file, "/", false ); FileWriter fos = new FileWriter( targetDir + '/' + t[t.length - 1] + ".xml" ); fos.write( ( (Marshallable) sld ).exportAsXML() ); fos.close(); } } /** * * * @version $Revision: 1.18 $ * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> */ private static class DFileFilter implements FilenameFilter { /** * @return */ public boolean accept( File f, String name ) { int pos = name.lastIndexOf( "." ); String ext = name.substring( pos + 1 ); return ext.toUpperCase().equals( "AVL" ); } } } /******************************************************************************* * Changes to this class. What the people have been up to: $$Log: AVL2SLD.java,v $ * Changes to this class. What the people have been up to: $Revision 1.18 2006/11/22 12:13:20 poth * Changes to this class. What the people have been up to: $*** empty log message *** * Changes to this class. What the people have been up to: $ * Changes to this class. What the people have been up to: $Revision 1.15 2006/08/24 06:43:54 poth * Changes to this class. What the people have been up to: $File header corrected * Changes to this class. What the people have been up to: $ * Changes to this class. What the people have been up to: $Revision 1.14 2006/08/23 14:11:53 ncho * Changes to this class. What the people have been up to: $added default values to create createLineRule. * Changes to this class. What the people have been up to: $changed default targetDir. * Changes to this class. What the people have been up to: $Class tested. * Changes to this class. What the people have been up to: $ * $Revision 1.13 2006/08/16 14:29:13 ncho $ported from deegree1 not yet tested $$ ******************************************************************************/