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