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 }