001 //$$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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 }