001 //$$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/tools/shape/AVL2SLD.java $$ 002 /*---------------- FILE HEADER ------------------------------------------ 003 This file is part of deegree. 004 Copyright (C) 2001-2008 by: 005 Department of Geography, University of Bonn 006 http://www.giub.uni-bonn.de/deegree/ 007 lat/lon GmbH 008 http://www.lat-lon.de 009 010 This library is free software; you can redistribute it and/or 011 modify it under the terms of the GNU Lesser General Public 012 License as published by the Free Software Foundation; either 013 version 2.1 of the License, or (at your option) any later version. 014 015 This library is distributed in the hope that it will be useful, 016 but WITHOUT ANY WARRANTY; without even the implied warranty of 017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 018 Lesser General Public License for more details. 019 020 You should have received a copy of the GNU Lesser General Public 021 License along with this library; if not, write to the Free Software 022 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 023 024 Contact: 025 026 Andreas Poth 027 lat/lon GmbH 028 Aennchenstraße 19 029 53177 Bonn 030 Germany 031 E-Mail: poth@lat-lon.de 032 033 Prof. Dr. Klaus Greve 034 Department of Geography 035 University of Bonn 036 Meckenheimer Allee 166 037 53115 Bonn 038 Germany 039 E-Mail: greve@giub.uni-bonn.de 040 041 ---------------------------------------------------------------------------*/ 042 043 package org.deegree.tools.shape; 044 045 import java.awt.Color; 046 import java.awt.image.BufferedImage; 047 import java.io.File; 048 import java.io.FileOutputStream; 049 import java.io.FileReader; 050 import java.io.FileWriter; 051 import java.io.FilenameFilter; 052 import java.io.IOException; 053 import java.io.Reader; 054 import java.util.ArrayList; 055 import java.util.HashMap; 056 import java.util.List; 057 import java.util.Map; 058 import java.util.regex.Matcher; 059 import java.util.regex.Pattern; 060 061 import org.deegree.datatypes.QualifiedName; 062 import org.deegree.framework.util.ImageUtils; 063 import org.deegree.framework.util.StringTools; 064 import org.deegree.framework.xml.Marshallable; 065 import org.deegree.graphics.sld.AbstractLayer; 066 import org.deegree.graphics.sld.AbstractStyle; 067 import org.deegree.graphics.sld.ExternalGraphic; 068 import org.deegree.graphics.sld.FeatureTypeStyle; 069 import org.deegree.graphics.sld.Fill; 070 import org.deegree.graphics.sld.Font; 071 import org.deegree.graphics.sld.Graphic; 072 import org.deegree.graphics.sld.GraphicFill; 073 import org.deegree.graphics.sld.LabelPlacement; 074 import org.deegree.graphics.sld.LineSymbolizer; 075 import org.deegree.graphics.sld.Mark; 076 import org.deegree.graphics.sld.NamedLayer; 077 import org.deegree.graphics.sld.PointPlacement; 078 import org.deegree.graphics.sld.PointSymbolizer; 079 import org.deegree.graphics.sld.PolygonSymbolizer; 080 import org.deegree.graphics.sld.Rule; 081 import org.deegree.graphics.sld.Stroke; 082 import org.deegree.graphics.sld.StyleFactory; 083 import org.deegree.graphics.sld.StyledLayerDescriptor; 084 import org.deegree.graphics.sld.Symbolizer; 085 import org.deegree.graphics.sld.TextSymbolizer; 086 import org.deegree.io.shpapi.MainFile; 087 import org.deegree.io.shpapi.ShapeConst; 088 import org.deegree.model.filterencoding.ComplexFilter; 089 import org.deegree.model.filterencoding.Filter; 090 import org.deegree.model.filterencoding.FilterConstructionException; 091 import org.deegree.model.filterencoding.Literal; 092 import org.deegree.model.filterencoding.LogicalOperation; 093 import org.deegree.model.filterencoding.Operation; 094 import org.deegree.model.filterencoding.OperationDefines; 095 import org.deegree.model.filterencoding.PropertyIsCOMPOperation; 096 import org.deegree.model.filterencoding.PropertyName; 097 098 /** 099 * <p> 100 * This class converts ESRI *.avl files to OGC SLD documents. The current version of this tool isn't 101 * able to convert each and every construction that is possible with an *.avl file. But most of the 102 * common expressions will be mapped. 103 * </p> 104 * <p> 105 * Because SLD (version 1.0.0) does not know inline bitmap or fill pattern definition directly 106 * (maybe it is possible using some SVG tags) all polygon fill patterns must be converted to images 107 * that are written to the file system and referenced as external graphic by the created SLD style. 108 * The similar is true for symbol definitions. SLD just 'knowns' a few predefined symbols that are 109 * not able to capture all symbols known by ArcView. In this context deegree also will extend the 110 * well known symbol by using ASCII codes to references symbols defined in an available font. This 111 * will enable deegree to transform the ArcView option defining a symbol by using an ACSII character 112 * directly. At least even if the synthax for this is SLD compliant most SLD implementation probably 113 * won't be able to evaluate this. 114 * </p> 115 * 116 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a> 117 * @author last edited by: $Author: apoth $ 118 * 119 * @version $Revision: 9346 $, $Date: 2007-12-27 17:39:07 +0100 (Do, 27 Dez 2007) $ 120 */ 121 public class AVL2SLD { 122 123 private String fileRootName = null; 124 125 private String targetDir = null; 126 127 private Map blocks = new HashMap(); 128 129 private static AVLPointSymbolCodeList cl = null; 130 131 /** 132 * @param fileRootName 133 * @param targetDir 134 */ 135 public AVL2SLD( String fileRootName, String targetDir ) { 136 this.fileRootName = fileRootName; 137 this.targetDir = targetDir; 138 if ( !this.targetDir.endsWith( "/" ) ) { 139 this.targetDir = this.targetDir + "/"; 140 } 141 } 142 143 /** 144 * reads the avl file assigned to a class instance 145 * 146 * @throws IOException 147 */ 148 public void read() 149 throws IOException { 150 Reader reader = new FileReader( fileRootName + ".avl" ); 151 StringBuffer sb = new StringBuffer( 50000 ); 152 int c = 0; 153 while ( ( c = reader.read() ) > -1 ) { 154 if ( c == 9 ) 155 c = ' '; 156 // if ( c != 10 && c != 13) { 157 sb.append( (char) c ); 158 // } 159 } 160 reader.close(); 161 162 // create a entry in 'blocks' for each '(' ... ')' 163 // enclosed section 164 String[] s1 = splitavl( sb.toString() ); 165 166 for ( int i = 1; i < s1.length; i++ ) { 167 // write each KVP of section to a map and store it 168 // in the blocks map. 169 // the Class, Pattern and he Child key will be treated 170 // as array because it isn't unique 171 int pos = s1[i].indexOf( ' ' ); 172 if ( pos < 0 ) 173 continue; 174 String section = s1[i].substring( 0, pos ).trim(); 175 String[] s2 = StringTools.toArray( s1[i].substring( pos, s1[i].length() ), ":\n", false ); 176 Map<String,Object> block = new HashMap<String,Object>(); 177 for ( int j = 0; j < s2.length; j = j + 2 ) { 178 if ( s2[j].trim().equals( "Class" ) ) { 179 List<String> list = (List<String>)block.get( "Class" ); 180 if ( list == null ) { 181 list = new ArrayList<String>(); 182 } 183 list.add( s2[j + 1] ); 184 block.put( s2[j], list ); 185 } else if ( s2[j].trim().equals( "Child" ) ) { 186 List<String> list = (List<String>)block.get( "Child" ); 187 if ( list == null ) { 188 list = new ArrayList<String>(); 189 } 190 list.add( s2[j + 1] ); 191 block.put( s2[j], list ); 192 } else if ( s2[j].trim().equals( "Pattern" ) ) { 193 List<String> list = (List<String>)block.get( "Pattern" ); 194 if ( list == null ) { 195 list = new ArrayList<String>(); 196 } 197 list.add( s2[j + 1] ); 198 block.put( s2[j], list ); 199 } else if ( s2[j].trim().equals( "Bits" ) ) { 200 List<String> list = (List<String>)block.get( "Bits" ); 201 if ( list == null ) { 202 list = new ArrayList<String>(); 203 } 204 list.add( s2[j + 1] ); 205 block.put( s2[j], list ); 206 } else { 207 block.put( s2[j], s2[j + 1].trim() ); 208 } 209 } 210 blocks.put( section, block ); 211 } 212 213 } 214 215 /** 216 * returns a <tt>Style</tt>. For each class/child contained in a avl file one <tt>Rule</tt> 217 * will be created 218 * 219 * @return a <tt>Style</tt>. For each class/child contained in a avl file one <tt>Rule</tt> 220 * will be created 221 * @throws IOException 222 * @throws Exception 223 */ 224 public AbstractStyle getStyle() 225 throws IOException, Exception { 226 Map odb = (Map) blocks.get( "ODB.1" ); 227 String roots = (String) odb.get( "Roots" ); 228 Map legend = (Map) blocks.get( "Legend." + roots ); 229 String filterCol = null; 230 if ( legend.get( "FieldNames" ) != null ) { 231 Map block = (Map) blocks.get( "AVStr." + legend.get( "FieldNames" ) ); 232 filterCol = (String) block.get( "S" ); 233 filterCol = StringTools.validateString( filterCol, "\"" ).toUpperCase(); 234 } 235 236 int geoType = getGeometryType(); 237 238 AbstractStyle style = null; 239 switch ( geoType ) { 240 case ShapeConst.SHAPE_TYPE_POINT: 241 style = createPointStyle( legend, filterCol ); 242 break; 243 case ShapeConst.SHAPE_TYPE_POLYLINE: 244 style = createLinesStyle( legend, filterCol ); 245 break; 246 case ShapeConst.SHAPE_TYPE_POLYGON: 247 style = createPolygonStyle( legend, filterCol ); 248 break; 249 case ShapeConst.SHAPE_TYPE_MULTIPOINT: 250 style = createPointStyle( legend, filterCol ); 251 break; 252 default: 253 throw new Exception( "unknown geometry type: " + geoType ); 254 } 255 return style; 256 } 257 258 /** 259 * creates a <tt>StyledLayerDescriptor</tt> from the avl file assigned to the instace of a 260 * <tt>AVLReader</tt>. The returned instance of a <tt>StyledLayerDescriptor</tt> just 261 * contains one style that may containes several <tt>Rule</tt>s 262 * 263 * @return a <tt>StyledLayerDescriptor</tt> created from the avl file assigned to the instace 264 * of a <tt>AVLReader</tt>. The returned instance of a <tt>StyledLayerDescriptor</tt> 265 * just contains one style that may containes several <tt>Rule</tt>s 266 * @throws IOException 267 * @throws Exception 268 */ 269 public StyledLayerDescriptor getStyledLayerDescriptor() 270 throws IOException, Exception { 271 AbstractStyle style = getStyle(); 272 String[] t = StringTools.toArray( fileRootName, "/", false ); 273 String name = "default:" + t[t.length - 1]; 274 AbstractLayer layer = new NamedLayer( name, null, new AbstractStyle[] { style } ); 275 return new StyledLayerDescriptor( new AbstractLayer[] { layer }, "1.0.0" ); 276 } 277 278 /** 279 * parse a string and return array of blocks between braces "(" and ")". It accounts for braces 280 * in quoted strings. 281 * 282 * @param s 283 * string to parse 284 * @return 285 */ 286 public static String[] splitavl( String s ) { 287 if ( s == null || s.equals( "" ) ) { 288 return new String[0]; 289 } 290 291 Pattern pat = Pattern.compile( "\\(([^)\"]|\"[^\"]*\")*\\)" ); 292 Matcher mat = pat.matcher( s ); 293 int prevend = 0; 294 ArrayList<String> vec = new ArrayList<String>(); 295 while ( mat.find() ) { 296 int start = mat.start(); 297 int end = mat.end(); 298 if ( prevend < start - 1 ) { 299 String str = s.substring( prevend, start ).trim(); 300 if ( str.length() > 0 ) { 301 vec.add( str ); 302 } 303 } 304 String str = s.substring( start + 1, end - 1 ).trim(); 305 if ( str.length() > 0 ) { 306 vec.add( str ); 307 } 308 prevend = end; 309 } 310 if ( prevend < s.length() - 1 ) { 311 String str = s.substring( prevend ).trim(); 312 if ( str.length() > 0 ) { 313 vec.add( str ); 314 } 315 } 316 317 // no value selected 318 if ( vec.size() == 0 ) { 319 return new String[0]; 320 } 321 322 return vec.toArray( new String[vec.size()] ); 323 } 324 325 private int getGeometryType() 326 throws IOException { 327 MainFile mf = new MainFile( fileRootName ); 328 int type = mf.getShapeTypeByRecNo( 1 ); 329 mf.close(); 330 return type; 331 } 332 333 /** 334 * creates a <tt>Style</tt>. For each class/child contained in a avl file one <tt>Rule</tt> 335 * will be created 336 * 337 * @param legend 338 * @param filterCol 339 * @return a <tt>Style</tt>. 340 */ 341 private AbstractStyle[] createPointStyles( Map legend, String filterCol ) { 342 AbstractStyle[] styles = null; 343 return styles; 344 } 345 346 /** 347 * creates a <tt>Style</tt>. For each class/child contained in a avl file one <tt>Rule</tt> 348 * will be created 349 * 350 * @param legend 351 * @param filterCol 352 * @return a <tt>Style</tt>. 353 */ 354 private AbstractStyle createPointStyle( Map legend, String filterCol ) 355 throws FilterConstructionException { 356 try { 357 cl = new AVLPointSymbolCodeList(); 358 } catch ( Exception e ) { 359 e.printStackTrace(); 360 } 361 String tmp = (String) legend.get( "Symbols" ); 362 Map block = (Map) blocks.get( "SymList." + tmp ); 363 List classes = (List) legend.get( "Class" ); 364 List children = (List) block.get( "Child" ); 365 366 List<Rule> list = new ArrayList<Rule>( classes.size() ); 367 for ( int i = 0; i < classes.size(); i++ ) { 368 String clNo = (String) classes.get( i ); 369 Map clss = (Map) blocks.get( "LClass." + clNo ); 370 String childNo = (String) children.get( i ); 371 Map child = (Map) blocks.get( "CMkSym." + childNo ); 372 Rule rule = null; 373 if ( child == null ) { 374 child = (Map) blocks.remove( "BMkSym." + childNo ); 375 rule = createSimplePointRule( clss, child, filterCol ); 376 } else { 377 rule = createComplexPointRule( clss, child, filterCol ); 378 } 379 if ( rule != null ) { 380 list.add( rule ); 381 } 382 } 383 Rule[] rules = list.toArray( new Rule[list.size()] ); 384 FeatureTypeStyle fts = StyleFactory.createFeatureTypeStyle( rules ); 385 386 String[] t = StringTools.toArray( fileRootName, "/", false ); 387 String name = "default:" + t[t.length - 1]; 388 return StyleFactory.createStyle( name, null, null, fts ); 389 } 390 391 /** 392 * creates a Style for a line symbol 393 * 394 * @param clss 395 * @param child 396 * @return a Style for a line symbol 397 */ 398 private Rule createSimplePointRule( Map clss, Map child, String filterCol ) { 399 400 if ( clss.get( "IsNoData" ) != null ) { 401 return null; 402 } 403 404 String label = (String) clss.get( "Label" ); 405 label = StringTools.validateString( label, "\"" ); 406 Filter filter = createFilter( clss, filterCol ); 407 // get foreground color 408 String clrIdx = (String) child.get( "Color" ); 409 Map color = (Map) blocks.get( "TClr." + clrIdx ); 410 double fgOpacity = 1f; 411 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { 412 fgOpacity = 0f; 413 } 414 Color fgColor = createColor( color ); 415 if ( fgColor == null ) { 416 fgColor = Color.BLACK; 417 } 418 // get background color (what ever this means) 419 clrIdx = (String) child.get( "BgColor" ); 420 color = (Map) blocks.get( "TClr." + clrIdx ); 421 double bgOpacity = 1f; 422 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { 423 bgOpacity = 0f; 424 } 425 Color bgColor = createColor( color ); 426 if ( bgColor == null ) { 427 bgColor = Color.BLACK; 428 } 429 430 double size = Double.parseDouble( (String) child.get( "Size" ) ); 431 double rotation = Double.parseDouble( (String) child.get( "Angle" ) ); 432 433 // creation of a font from a avl file is a triky thing because 434 // esri uses their own font names and codes 435 // --> don' know if this works 436 String fntIdx = (String) child.get( "Font" ); 437 Map fntMap = (Map) blocks.get( "NFont." + fntIdx ); 438 Font font = createFont( fntMap ); 439 PointPlacement pp = StyleFactory.createPointPlacement(); 440 LabelPlacement labelPlacement = StyleFactory.createLabelPlacement( pp ); 441 TextSymbolizer textSym = StyleFactory.createTextSymbolizer( fgColor, font, filterCol, 442 labelPlacement ); 443 444 String patternIdx = (String) ( (ArrayList) child.get( "Pattern" ) ).get( 0 ); 445 String symbol = cl.getSymbol( patternIdx ); 446 447 // create a Mark with a stroke and fill to controll both 448 // opacities 449 Stroke stroke = StyleFactory.createStroke( fgColor, 1, fgOpacity, null, "mitre", "butt" ); 450 Fill fill = StyleFactory.createFill( bgColor, bgOpacity ); 451 Mark mark = StyleFactory.createMark( symbol, fill, stroke ); 452 453 Graphic graphic = StyleFactory.createGraphic( null, mark, 1, size, rotation ); 454 PointSymbolizer pointSym = StyleFactory.createPointSymbolizer( graphic ); 455 456 return StyleFactory.createRule( new Symbolizer[] { pointSym, textSym }, label, label, "", 457 null, filter, false, 0, 9E99 ); 458 } 459 460 /** 461 * 462 * @param clss 463 * @param child 464 * @param filterCol 465 * @return 466 * @throws FilterConstructionException 467 */ 468 private Rule createComplexPointRule( Map clss, Map child, String filterCol ) 469 throws FilterConstructionException { 470 471 if ( clss.get( "IsNoData" ) != null ) { 472 return null; 473 } 474 475 String tmp = (String) child.get( "Symbols" ); 476 Map block = (Map) blocks.get( "SymList." + tmp ); 477 List children = (List) block.get( "Child" ); 478 479 List<Symbolizer> smbls = new ArrayList<Symbolizer>(); 480 for ( int i = 0; i < children.size(); i++ ) { 481 String childNo = (String) children.get( i ); 482 Map child_ = (Map) blocks.get( "CMkSym." + childNo ); 483 Rule rule = null; 484 if ( child_ == null ) { 485 child = (Map) blocks.remove( "BMkSym." + childNo ); 486 rule = createSimplePointRule( clss, child, filterCol ); 487 } else { 488 rule = createComplexPointRule( clss, child, filterCol ); 489 } 490 Symbolizer[] sym = rule.getSymbolizers(); 491 for ( int j = 0; j < sym.length; j++ ) { 492 smbls.add( sym[j] ); 493 } 494 } 495 Symbolizer[] sym = new Symbolizer[smbls.size()]; 496 sym = smbls.toArray( sym ); 497 498 String label = (String) clss.get( "Label" ); 499 label = StringTools.validateString( label, "\"" ); 500 Filter filter = createFilter( clss, filterCol ); 501 502 return StyleFactory.createRule( sym, label, label, "", null, filter, false, 0, 9E99 ); 503 } 504 505 private Font createFont( Map fntMap ) { 506 String idx = (String) fntMap.get( "Family" ); 507 String family = (String) ( (Map) blocks.get( "AVStr." + idx ) ).get( "S" ); 508 idx = (String) fntMap.get( "Name" ); 509 String name = (String) ( (Map) blocks.get( "AVStr." + idx ) ).get( "S" ); 510 idx = (String) fntMap.get( "Style" ); 511 String style = (String) ( (Map) blocks.get( "AVStr." + idx ) ).get( "S" ); 512 String weight = (String) fntMap.get( "Weight" ); 513 String wideness = (String) fntMap.get( "Wideness" ); 514 515 boolean italic = style.equals( "Italic" ); 516 boolean bold = Integer.parseInt( weight ) > 1; 517 518 return StyleFactory.createFont( family, italic, bold, 12 ); 519 } 520 521 /** 522 * creates a <tt>Style</tt>. For each class/child contained in a avl file one <tt>Rule</tt> 523 * will be created 524 * 525 * @param legend 526 * @param filterCol 527 * @return a <tt>Style</tt>. 528 */ 529 private AbstractStyle createLinesStyle( Map legend, String filterCol ) { 530 String tmp = (String) legend.get( "Symbols" ); 531 Map block = (Map) blocks.get( "SymList." + tmp ); 532 List classes = (List) legend.get( "Class" ); 533 List children = (List) block.get( "Child" ); 534 List<Rule> list = new ArrayList<Rule>( classes.size() ); 535 for ( int i = 0; i < classes.size(); i++ ) { 536 String clNo = (String) classes.get( i ); 537 Map clss = (Map) blocks.get( "LClass." + clNo ); 538 String childNo = (String) children.get( i ); 539 Map child = (Map) blocks.get( "BLnSym." + childNo ); 540 541 if ( child == null ) { 542 // won't be treated correctly because we can't transform 543 // lines with sambols at the moment 544 child = (Map) blocks.get( "CLnSym." + childNo ); 545 } 546 Rule rule = createLineRule( clss, child, filterCol ); 547 if ( rule != null ) { 548 list.add( rule ); 549 } 550 } 551 Rule[] rules = list.toArray( new Rule[list.size()] ); 552 FeatureTypeStyle fts = StyleFactory.createFeatureTypeStyle( rules ); 553 554 String[] t = StringTools.toArray( fileRootName, "/", false ); 555 String name = "default:" + t[t.length - 1]; 556 return StyleFactory.createStyle( name, null, null, fts ); 557 } 558 559 /** 560 * creates a Style for a line symbol 561 * 562 * @param clss 563 * @param child 564 * @return a Style for a line symbol 565 */ 566 private Rule createLineRule( Map clss, Map child, String filterCol ){ 567 568 if ( clss.get( "IsNoData" ) != null ) { 569 return null; 570 } 571 572 String label = (String) clss.get( "Label" ); 573 label = StringTools.validateString( label, "\"" ); 574 Filter filter = createFilter( clss, filterCol ); 575 576 String clrIdx = (String) child.get( "Color" ); 577 Map color = (Map) blocks.get( "TClr." + clrIdx ); 578 double opacity = 1f; 579 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) && color.size() > 0 ) { 580 opacity = 0f; 581 } 582 Color colour = createColor( color ); 583 if ( colour == null ) { 584 colour = Color.BLACK; 585 } 586 587 double width = 1.0; // default Width 588 if ( child.get( "Width" ) != null ) { 589 width = Double.parseDouble( (String) child.get( "Width" ) ) + width; 590 } 591 // double width = Double.parseDouble( (String)child.get("Width") ) + 1; 592 List pl = (List) child.get( "Pattern" ); 593 594 if ( child.get( "Pattern" ) == null ) { // create a default pattern List if 595 // it is null 596 pl = new ArrayList<String>(); 597 for ( int i = 0; i < 16; i++ ) { // Fill the default List with 598 // default values "0.00000000000000" 599 pl.add( "0.00000000000000" ); 600 } 601 } 602 603 // List pl = (List)child.get("Pattern"); 604 float[] dashArray = createDashArray( pl ); 605 Stroke stroke = StyleFactory.createStroke( colour, width, opacity, dashArray, "mitre", 606 "butt" ); 607 LineSymbolizer lineSym = StyleFactory.createLineSymbolizer( stroke ); 608 609 return StyleFactory.createRule( new Symbolizer[] { lineSym }, label, label, "", null, 610 filter, false, 0, 9E99 ); 611 } 612 613 /** 614 * creates a <tt>Style</tt>. For each class/child contained in a avl file one <tt>Rule</tt> 615 * will be created 616 * 617 * @param legend 618 * @param filterCol 619 * @return a <tt>Style</tt>. 620 */ 621 private AbstractStyle createPolygonStyle( Map legend, String filterCol ) 622 throws Exception { 623 String tmp = (String) legend.get( "Symbols" ); 624 Map block = (Map) blocks.get( "SymList." + tmp ); 625 List classes = (List) legend.get( "Class" ); 626 List children = (List) block.get( "Child" ); 627 List<Rule> list = new ArrayList<Rule>( classes.size() ); 628 for ( int i = 0; i < classes.size(); i++ ) { 629 String clNo = (String) classes.get( i ); 630 Map clss = (Map) blocks.get( "LClass." + clNo ); 631 String childNo = (String) children.get( i ); 632 Map child = (Map) blocks.get( "BShSym." + childNo ); 633 Rule rule = null; 634 if ( child == null ) { 635 // VShSym is a vector polygon fill 636 child = (Map) blocks.get( "VShSym." + childNo ); 637 rule = createPolygonVecRule( clss, child, filterCol ); 638 } else { 639 rule = createPolygonBMPRule( clss, child, filterCol ); 640 } 641 if ( rule != null ) { 642 list.add( rule ); 643 } 644 // TODO 645 // write special method for vector polygon fill 646 } 647 Rule[] rules = list.toArray( new Rule[list.size()] ); 648 FeatureTypeStyle fts = StyleFactory.createFeatureTypeStyle( rules ); 649 650 String[] t = StringTools.toArray( fileRootName, "/", false ); 651 String name = "default:" + t[t.length - 1]; 652 return StyleFactory.createStyle( name, null, null, fts ); 653 } 654 655 /** 656 * creates a Style for a line symbol 657 * 658 * @param clss 659 * @param child 660 * @return a Style for a line symbol 661 */ 662 private Rule createPolygonBMPRule( Map clss, Map child, String filterCol ) 663 throws Exception { 664 665 if ( clss.get( "IsNoData" ) != null ) { 666 return null; 667 } 668 669 String label = (String) clss.get( "Label" ); 670 label = StringTools.validateString( label, "\"" ); 671 672 Filter filter = null; 673 if ( filterCol != null ) { 674 filter = createFilter( clss, filterCol ); 675 } 676 // get foreground color 677 String clrIdx = (String) child.get( "Color" ); 678 Map color = (Map) blocks.get( "TClr." + clrIdx ); 679 double opacity = 1f; 680 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { 681 opacity = 0f; 682 } 683 684 Color fgColor = createColor( color ); 685 if ( fgColor == null ) { 686 fgColor = Color.BLACK; 687 } 688 // get color of the outlining stroke 689 clrIdx = (String) child.get( "OutlineColor" ); 690 color = (Map) blocks.get( "TClr." + clrIdx ); 691 double outLOpacity = 1f; 692 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { 693 outLOpacity = 0f; 694 } 695 Color outLColor = createColor( color ); 696 if ( outLColor == null ) { 697 outLColor = Color.BLACK; 698 } 699 // get background color 700 clrIdx = (String) child.get( "BgColor" ); 701 color = (Map) blocks.get( "TClr." + clrIdx ); 702 double bgOpacity = 1f; 703 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { 704 bgOpacity = 0f; 705 } 706 Color bgColor = createColor( color ); 707 708 // create fill pattern as an image that will be referenced as 709 // external graphic 710 String stippleIdx = (String) child.get( "Stipple" ); 711 String src = null; 712 if ( stippleIdx != null ) { 713 Map stipple = (Map) blocks.get( "Stipple." + stippleIdx ); 714 src = createExternalGraphicFromStipple( stipple, label, fgColor, bgColor ); 715 } 716 717 double width = Double.parseDouble( (String) child.get( "OutlineWidth" ) ) + 1; 718 Stroke stroke = StyleFactory.createStroke( outLColor, width, outLOpacity, null, "mitre", 719 "butt" ); 720 Fill fill = null; 721 if ( stippleIdx != null ) { 722 ExternalGraphic eg = StyleFactory.createExternalGraphic( "file:///" + src, "image/gif" ); 723 Graphic graph = StyleFactory.createGraphic( eg, null, opacity, 10, 0 ); 724 GraphicFill gf = StyleFactory.createGraphicFill( graph ); 725 fill = StyleFactory.createFill( fgColor, opacity, gf ); 726 } else { 727 fill = StyleFactory.createFill( fgColor, opacity ); 728 } 729 PolygonSymbolizer polySym = StyleFactory.createPolygonSymbolizer( stroke, fill ); 730 return StyleFactory.createRule( new Symbolizer[] { polySym }, label, label, "", null, 731 filter, false, 0, 9E99 ); 732 } 733 734 /** 735 * creates a Style for a line symbol 736 * 737 * @param clss 738 * @param child 739 * @return a Style for a line symbol 740 */ 741 private Rule createPolygonVecRule( Map clss, Map child, String filterCol ) 742 throws Exception { 743 744 if ( clss.get( "IsNoData" ) != null ) { 745 return null; 746 } 747 748 String label = (String) clss.get( "Label" ); 749 label = StringTools.validateString( label, "\"" ); 750 751 Filter filter = null; 752 if ( filterCol != null ) { 753 filter = createFilter( clss, filterCol ); 754 } 755 // get foreground color 756 String clrIdx = (String) child.get( "Color" ); 757 Map color = (Map) blocks.get( "TClr." + clrIdx ); 758 double opacity = 1f; 759 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { 760 opacity = 0f; 761 } 762 Color fgColor = createColor( color ); 763 if ( fgColor == null ) { 764 fgColor = Color.BLACK; 765 } 766 // get color of the outlining stroke 767 clrIdx = (String) child.get( "OutlineColor" ); 768 color = (Map) blocks.get( "TClr." + clrIdx ); 769 double outLOpacity = 1f; 770 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { 771 outLOpacity = 0f; 772 } 773 Color outLColor = createColor( color ); 774 if ( outLColor == null ) { 775 outLColor = Color.BLACK; 776 } 777 // get background color 778 clrIdx = (String) child.get( "BgColor" ); 779 color = (Map) blocks.get( "TClr." + clrIdx ); 780 double bgOpacity = 1f; 781 if ( color != null && "\"Transparent\"".equals( color.get( "Name" ) ) ) { 782 bgOpacity = 0f; 783 } 784 Color bgColor = createColor( color ); 785 786 // create fill pattern as an image that will be referenced as 787 // external graphic 788 String stippleIdx = (String) child.get( "Stipple" ); 789 String src = null; 790 if ( stippleIdx != null ) { 791 Map stipple = (Map) blocks.get( "Stipple." + stippleIdx ); 792 src = createExternalGraphicFromStipple( stipple, label, fgColor, bgColor ); 793 } 794 795 double width = Double.parseDouble( (String) child.get( "OutlineWidth" ) ) + 1; 796 Stroke stroke = StyleFactory.createStroke( outLColor, width, outLOpacity, null, "mitre", 797 "butt" ); 798 Fill fill = null; 799 if ( stippleIdx != null ) { 800 ExternalGraphic eg = StyleFactory.createExternalGraphic( "file:///" + src, "image/gif" ); 801 Graphic graph = StyleFactory.createGraphic( eg, null, opacity, 10, 0 ); 802 GraphicFill gf = StyleFactory.createGraphicFill( graph ); 803 fill = StyleFactory.createFill( fgColor, opacity, gf ); 804 } else { 805 fill = StyleFactory.createFill( fgColor, opacity ); 806 } 807 PolygonSymbolizer polySym = StyleFactory.createPolygonSymbolizer( stroke, fill ); 808 return StyleFactory.createRule( new Symbolizer[] { polySym }, label, label, "", null, 809 filter, false, 0, 9E99 ); 810 } 811 812 /** 813 * creates an image from a stipple and stores it as a gif image. The method returns the full 814 * name of the stored image. 815 * 816 * @param stipple 817 * @return an image from a stipple and stores it as a gif image. The method returns the full 818 * name of the stored image. 819 */ 820 private String createExternalGraphicFromStipple( Map stipple, String label, Color fg, Color bg ) 821 throws Exception { 822 823 if ( label != null ) { 824 label = label.replace( ' ', '_' ); 825 label = label.replace( '.', '_' ); 826 label = label.replace( ';', '_' ); 827 label = label.replace( ',', '_' ); 828 label = label.replace( '-', '_' ); 829 label = label.replace( ':', '_' ); 830 } 831 String tmp = (String) stipple.get( "Columns" ); 832 int cols = Integer.parseInt( tmp ); 833 tmp = (String) stipple.get( "Rows" ); 834 int rows = Integer.parseInt( tmp ); 835 836 List bList = (List) stipple.get( "Bits" ); 837 StringBuffer bStr = new StringBuffer( 1000 ); 838 for ( int i = 0; i < bList.size(); i++ ) { 839 String[] t = StringTools.toArray( ( (String) bList.get( i ) ).trim(), " ", false ); 840 for ( int j = 0; j < t.length; j++ ) { 841 bStr.append( t[j] ); 842 } 843 } 844 845 char[] ch = bStr.toString().toCharArray(); 846 847 BufferedImage bi = createFillPattern( cols, rows, ch, fg, bg ); 848 849 final String format = "gif"; 850 851 String[] t = StringTools.toArray( fileRootName, "/", false ); 852 String base = t[t.length - 1]; 853 StringBuffer fileName = new StringBuffer(); 854 fileName.append( targetDir ).append( base ); 855 if ( label != null ) { 856 fileName.append( "_" ).append( label ).append( "." ).append( format ); 857 } else { 858 fileName.append( "." ).append( format ); 859 } 860 861 // FileOutputStream fos = new FileOutputStream( targetDir + base + '_' + label + "." + 862 // format ); 863 FileOutputStream fos = new FileOutputStream( fileName.toString() ); 864 ImageUtils.saveImage( bi, fos, format, 1.0f ); 865 // Encoders.encodeGif(fos,bi); 866 fos.close(); 867 868 if ( targetDir.startsWith( "/" ) ) { 869 if ( label != null ) { 870 return targetDir.substring( 1, targetDir.length() ) + base + '_' + label + "." 871 + format; 872 } else { 873 return targetDir.substring( 1, targetDir.length() ) + base + "." + format; 874 } 875 } else { 876 if ( label != null ) { 877 return targetDir + base + '_' + label + "." + format; 878 } else { 879 return targetDir + base + "." + format; 880 } 881 882 } 883 884 } 885 886 /** 887 * creates a <tt>BufferedImage</tt> using the stipple contained in the passed char array. 888 * 889 * @param cols 890 * @param rows 891 * @param ch 892 * @return a <tt>BufferedImage</tt> using the stipple contained in the passed char array. 893 */ 894 private BufferedImage createFillPattern( int cols, int rows, char[] ch, Color fg, Color bg ) { 895 BufferedImage bi = new BufferedImage( cols, rows, BufferedImage.TYPE_INT_ARGB ); 896 int cntChar = 0; 897 byte[] bTmp = null; 898 char chr = ' '; 899 int hexCnt = 0; 900 if ( cols % 8 != 0 ) { 901 hexCnt = ( cols / 8 + 1 ) * 8; 902 } else { 903 hexCnt = cols; 904 } 905 for ( int i = 0; i < rows; i++ ) { 906 for ( int j = 0; j < hexCnt; j++ ) { 907 if ( j % 4 == 0 ) { 908 chr = ch[cntChar++]; 909 bTmp = getBits( chr ); 910 } 911 if ( j < cols ) { 912 if ( bTmp[j % 4] == 0 ) { 913 if ( bg != null ) { 914 bi.setRGB( j, i, bg.getRGB() ); 915 } 916 } else { 917 bi.setRGB( j, i, fg.getRGB() ); 918 } 919 } 920 } 921 } 922 return bi; 923 } 924 925 private byte[] getBits( int ch ) { 926 switch ( ch ) { 927 case '0': 928 return new byte[] { 0, 0, 0, 0 }; 929 case '1': 930 return new byte[] { 0, 0, 0, 1 }; 931 case '2': 932 return new byte[] { 0, 0, 1, 0 }; 933 case '3': 934 return new byte[] { 0, 0, 1, 1 }; 935 case '4': 936 return new byte[] { 0, 1, 0, 0 }; 937 case '5': 938 return new byte[] { 0, 1, 0, 1 }; 939 case '6': 940 return new byte[] { 0, 1, 1, 0 }; 941 case '7': 942 return new byte[] { 0, 1, 1, 1 }; 943 case '8': 944 return new byte[] { 1, 0, 0, 0 }; 945 case '9': 946 return new byte[] { 1, 0, 0, 1 }; 947 case 'a': 948 return new byte[] { 1, 0, 1, 0 }; 949 case 'b': 950 return new byte[] { 1, 0, 1, 1 }; 951 case 'c': 952 return new byte[] { 1, 1, 0, 0 }; 953 case 'd': 954 return new byte[] { 1, 1, 0, 1 }; 955 case 'e': 956 return new byte[] { 1, 1, 1, 0 }; 957 case 'f': 958 return new byte[] { 1, 1, 1, 1 }; 959 default: 960 return new byte[] { 0, 0, 0, 0 }; 961 } 962 } 963 964 /** 965 * creates a dasharray for constructing a stroke from the pattern entries of a avl xxxSym. block 966 * 967 * @param pl 968 * @return 969 */ 970 private float[] createDashArray( List pl ) { 971 int cnt = 0; 972 for ( int i = 0; i < pl.size(); i++ ) { 973 if ( Float.parseFloat( (String) pl.get( i ) ) > 0 ) { 974 cnt++; 975 } else { 976 break; 977 } 978 } 979 float[] pattern = null; 980 if ( cnt > 0 ) { 981 pattern = new float[cnt]; 982 for ( int i = 0; i < pattern.length; i++ ) { 983 pattern[i] = Float.parseFloat( (String) pl.get( i ) ); 984 } 985 } 986 return pattern; 987 } 988 989 /** 990 * creates a AWT color from a val color block 991 * 992 * @param color 993 * @return a AWT color from a val color block 994 */ 995 private Color createColor( Map color ) { 996 StringBuffer hex = new StringBuffer( "0x" ); 997 if ( color != null && !"\"Transparent\"".equals( color.get( "Name" ) ) ) { 998 String red = (String) color.get( "Red" ); 999 if ( red == null ) 1000 red = "0x0000"; 1001 int c = (int) ( ( Integer.decode( red ).intValue() / 65535f ) * 255 ); 1002 if ( c < 15 ) { 1003 hex.append( 0 ); 1004 } 1005 hex.append( Integer.toHexString( c ) ); 1006 String green = (String) color.get( "Green" ); 1007 if ( green == null ) 1008 green = "0x0000"; 1009 c = (int) ( ( Integer.decode( green ).intValue() / 65535f ) * 255 ); 1010 if ( c < 15 ) { 1011 hex.append( 0 ); 1012 } 1013 hex.append( Integer.toHexString( c ) ); 1014 String blue = (String) color.get( "Blue" ); 1015 if ( blue == null ) 1016 blue = "0x0000"; 1017 c = (int) ( ( Integer.decode( blue ).intValue() / 65535f ) * 255 ); 1018 if ( c < 15 ) { 1019 hex.append( 0 ); 1020 } 1021 hex.append( Integer.toHexString( c ) ); 1022 } else { 1023 // hex.append("000000"); 1024 return null; 1025 } 1026 return Color.decode( hex.toString() ); 1027 } 1028 1029 /** 1030 * 1031 * @param clss 1032 * @param filterCol 1033 * @return 1034 * @throws FilterConstructionException 1035 */ 1036 private Filter createFilter( Map clss, String filterCol ) { 1037 1038 if ( clss.get( "Label" ) == null ) { 1039 return null; 1040 } 1041 Filter filter = null; 1042 1043 String maxN = (String) clss.get( "MaxStr" ); 1044 String minN = (String) clss.get( "MinStr" ); 1045 if ( maxN != null && minN != null ) { 1046 filter = createStringFilter( clss, filterCol ); 1047 } else { 1048 filter = createNumberFilter( clss, filterCol ); 1049 } 1050 1051 return filter; 1052 } 1053 1054 /** 1055 * @param filterCol 1056 * @param maxN 1057 * @param minN 1058 * @return 1059 * @throws FilterConstructionException 1060 */ 1061 private Filter createStringFilter( Map clss, String filterCol ) { 1062 Filter filter; 1063 Operation oper = null; 1064 String maxN = (String) clss.get( "MaxStr" ); 1065 String minN = (String) clss.get( "MinStr" ); 1066 maxN = maxN.substring( 1, maxN.length() - 1 ); 1067 minN = minN.substring( 1, minN.length() - 1 ); 1068 if ( maxN.equals( minN ) ) { 1069 oper = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, 1070 new PropertyName( new QualifiedName( filterCol ) ), new Literal( minN ) ); 1071 } else { 1072 ArrayList<Operation> list = new ArrayList<Operation>(); 1073 Operation op = new PropertyIsCOMPOperation( 1074 OperationDefines.PROPERTYISLESSTHANOREQUALTO, 1075 new PropertyName( new QualifiedName( filterCol ) ), 1076 new Literal( maxN ) ); 1077 list.add( op ); 1078 op = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISGREATERTHANOREQUALTO, 1079 new PropertyName( new QualifiedName( filterCol ) ), new Literal( minN ) ); 1080 list.add( op ); 1081 1082 oper = new LogicalOperation( OperationDefines.AND, list ); 1083 } 1084 filter = new ComplexFilter( oper ); 1085 return filter; 1086 } 1087 1088 /** 1089 * @param clss 1090 * @param filterCol 1091 * @return 1092 * @throws FilterConstructionException 1093 */ 1094 private Filter createNumberFilter( Map clss, String filterCol ) { 1095 1096 String maxN = (String) clss.get( "MaxNum" ); 1097 String minN = (String) clss.get( "MinNum" ); 1098 if ( maxN == null ) { 1099 maxN = "9E99"; 1100 } 1101 if ( minN == null ) { 1102 minN = "-9E99"; 1103 } 1104 double t1 = Double.parseDouble( minN ); 1105 double t2 = Double.parseDouble( maxN ); 1106 Operation oper = null; 1107 if ( t1 == t2 ) { 1108 // if t1 == t2 no range is defined and so an 'is equal to' 1109 // opertaion must be used 1110 if ( ( (int) t1 ) == t1 ) { 1111 // if no significant fraction values are present 1112 // cast the value to int 1113 oper = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, 1114 new PropertyName( new QualifiedName( filterCol ) ), 1115 new Literal( "" + ( (int) t1 ) ) ); 1116 } else { 1117 oper = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISEQUALTO, 1118 new PropertyName( new QualifiedName( filterCol )), 1119 new Literal( "" + t1 ) ); 1120 } 1121 } else { 1122 // if t1 != t2 range of valid values is defined and so an so two 1123 // operation (one for each border of the range) are used 1124 ArrayList<Operation> list = new ArrayList<Operation>(); 1125 Operation op = new PropertyIsCOMPOperation( 1126 OperationDefines.PROPERTYISLESSTHANOREQUALTO, 1127 new PropertyName( new QualifiedName( filterCol ) ), 1128 new Literal( maxN ) ); 1129 list.add( op ); 1130 op = new PropertyIsCOMPOperation( OperationDefines.PROPERTYISGREATERTHANOREQUALTO, 1131 new PropertyName( new QualifiedName( filterCol ) ), new Literal( minN ) ); 1132 list.add( op ); 1133 1134 oper = new LogicalOperation( OperationDefines.AND, list ); 1135 1136 } 1137 1138 return new ComplexFilter( oper ); 1139 1140 } 1141 1142 private static String[] getAVLFiles( String dir ) { 1143 File file = new File( dir ); 1144 return file.list( new DFileFilter() ); 1145 1146 } 1147 1148 private static void printHelp() { 1149 1150 System.out.println( "Converts ESRI *.avl files to OGC SLD documents. The current version of " ); 1151 System.out.println( "this tool isn't able to convert each and every construction that is " ); 1152 System.out.println( "possible with an *.avl file. But most of the common expressions will be mapped." ); 1153 System.out.println( "Supported are *.avl files for point, lines and polygons." ); 1154 System.out.println( "" ); 1155 System.out.println( "-sourceFile -> full path to the *.avl file to be converted. " ); 1156 System.out.println( " in the same directory a shapefile with the same name as the *.avl" ); 1157 System.out.println( " file must exist! (conditional, -sourceFile or -sourceDir must " ); 1158 System.out.println( " be defined)" ); 1159 System.out.println( "-sourceDir -> directory containing one or more *.avl files. for each existing" ); 1160 System.out.println( " *.avl file a shapefile with the same base name must exist. " ); 1161 System.out.println( " (conditional, -sourceFile or -sourceDir must be defined)" ); 1162 System.out.println( "-targetDir -> directory where the created SLD document(s) will be stored. (optional)" ); 1163 System.out.println( "-h -> print this help" ); 1164 } 1165 1166 /** 1167 * 1168 * @param args 1169 * @throws Exception 1170 */ 1171 public static void main( String[] args ) 1172 throws Exception { 1173 1174 Map<String,String> map = new HashMap<String,String>(); 1175 1176 for ( int i = 0; i < args.length; i += 2 ) { 1177 map.put( args[i], args[i + 1] ); 1178 } 1179 1180 if ( map.get( "-sourceFile" ) == null && map.get( "-sourceDir" ) == null 1181 && map.get( "-h" ) == null ) { 1182 System.out.println( "-sourceFile or -sourceDir must be defined!" ); 1183 System.out.println(); 1184 printHelp(); 1185 System.exit( 1 ); 1186 } 1187 1188 if ( map.get( "-h" ) != null ) { 1189 printHelp(); 1190 System.exit( 0 ); 1191 } 1192 1193 String targetDir = "."; 1194 String[] sourceFiles = null; 1195 if ( map.get( "-sourceFile" ) != null ) { 1196 sourceFiles = new String[] { map.get( "-sourceFile" ) }; 1197 // set the default target directory to the sourceFile's directory 1198 targetDir = sourceFiles[0].substring( 0, sourceFiles[0].lastIndexOf( "/" ) ); 1199 } 1200 1201 if ( sourceFiles == null ) { 1202 String sourceDir = map.get( "-sourceDir" ); 1203 sourceFiles = getAVLFiles( sourceDir ); 1204 for ( int i = 0; i < sourceFiles.length; i++ ) { 1205 sourceFiles[i] = sourceDir + '/' + sourceFiles[i]; 1206 } 1207 // set the default target directory to the source directory 1208 targetDir = sourceDir; 1209 1210 } 1211 1212 // String targetDir = "."; 1213 if ( map.get( "-targetDir" ) != null ) { 1214 targetDir = map.get( "-targetDir" ); 1215 } 1216 1217 for ( int i = 0; i < sourceFiles.length; i++ ) { 1218 System.out.println( "processing: " + sourceFiles[i] ); 1219 int pos = sourceFiles[i].lastIndexOf( '.' ); 1220 String file = sourceFiles[i].substring( 0, pos ); 1221 AVL2SLD avl = new AVL2SLD( file, targetDir ); 1222 avl.read(); 1223 StyledLayerDescriptor sld = avl.getStyledLayerDescriptor(); 1224 String[] t = StringTools.toArray( file, "/", false ); 1225 FileWriter fos = new FileWriter( targetDir + '/' + t[t.length - 1] + ".xml" ); 1226 fos.write( ( (Marshallable) sld ).exportAsXML() ); 1227 fos.close(); 1228 } 1229 } 1230 1231 /** 1232 * 1233 * 1234 * @version $Revision: 9346 $ 1235 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 1236 */ 1237 private static class DFileFilter implements FilenameFilter { 1238 /** 1239 * @param f 1240 * @return 1241 */ 1242 public boolean accept( File f, String name ) { 1243 int pos = name.lastIndexOf( "." ); 1244 String ext = name.substring( pos + 1 ); 1245 return ext.toUpperCase().equals( "AVL" ); 1246 } 1247 } 1248 1249 }