001 //$HeadURL: https://sushibar/svn/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $
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.io.mapinfoapi;
038
039 import static java.io.StreamTokenizer.TT_EOF;
040 import static java.io.StreamTokenizer.TT_EOL;
041 import static java.lang.Double.parseDouble;
042 import static java.lang.Integer.parseInt;
043 import static java.nio.charset.Charset.defaultCharset;
044 import static java.nio.charset.Charset.forName;
045 import static org.deegree.datatypes.Types.BOOLEAN;
046 import static org.deegree.datatypes.Types.DATE;
047 import static org.deegree.datatypes.Types.DOUBLE;
048 import static org.deegree.datatypes.Types.FLOAT;
049 import static org.deegree.datatypes.Types.GEOMETRY;
050 import static org.deegree.datatypes.Types.INTEGER;
051 import static org.deegree.datatypes.Types.VARCHAR;
052 import static org.deegree.model.feature.FeatureFactory.createFeature;
053 import static org.deegree.model.feature.FeatureFactory.createFeatureCollection;
054 import static org.deegree.model.feature.FeatureFactory.createFeatureProperty;
055 import static org.deegree.model.feature.FeatureFactory.createFeatureType;
056 import static org.deegree.model.feature.FeatureFactory.createGeometryPropertyType;
057 import static org.deegree.model.feature.FeatureFactory.createSimplePropertyType;
058 import static org.deegree.model.spatialschema.GeometryFactory.createPoint;
059
060 import java.io.File;
061 import java.io.FileInputStream;
062 import java.io.FileNotFoundException;
063 import java.io.IOException;
064 import java.io.InputStreamReader;
065 import java.io.StreamTokenizer;
066 import java.io.UnsupportedEncodingException;
067 import java.net.URI;
068 import java.net.URISyntaxException;
069 import java.nio.charset.Charset;
070 import java.text.DateFormat;
071 import java.text.ParseException;
072 import java.text.SimpleDateFormat;
073 import java.util.Date;
074 import java.util.HashMap;
075 import java.util.HashSet;
076 import java.util.LinkedList;
077
078 import org.deegree.datatypes.QualifiedName;
079 import org.deegree.framework.log.ILogger;
080 import org.deegree.framework.log.LoggerFactory;
081 import org.deegree.framework.util.Pair;
082 import org.deegree.model.crs.CoordinateSystem;
083 import org.deegree.model.feature.Feature;
084 import org.deegree.model.feature.FeatureCollection;
085 import org.deegree.model.feature.FeatureProperty;
086 import org.deegree.model.feature.schema.FeatureType;
087 import org.deegree.model.feature.schema.PropertyType;
088 import org.deegree.model.spatialschema.Curve;
089 import org.deegree.model.spatialschema.MultiPoint;
090 import org.deegree.model.spatialschema.MultiSurface;
091 import org.deegree.model.spatialschema.Point;
092 import org.deegree.model.spatialschema.Surface;
093
094 /**
095 * <code>MapInfoReader</code> is a new implementation of a reader for the MID/MIF format, based on
096 * the MapInfo 8.5 documentation.
097 *
098 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
099 * @author last edited by: $Author:$
100 *
101 * @version $Revision:$, $Date:$
102 */
103 public class MapInfoReader {
104
105 private static final ILogger LOG = LoggerFactory.getLogger( MapInfoReader.class );
106
107 private static final URI APPNS;
108
109 private StreamTokenizer mif;
110
111 private StreamTokenizer mid;
112
113 private FeatureType featureType;
114
115 private char delimiter;
116
117 private File mifFile;
118
119 private File midFile;
120
121 private FeatureCollection featureCollection;
122
123 private LinkedList<Feature> features;
124
125 private MIFGeometryParser parser;
126
127 private MIFStyleParser styleParser;
128
129 private HashMap<String, HashSet<HashMap<String, String>>> styles;
130
131 private boolean featureTypeParsed, featuresParsed;
132
133 static {
134 URI u = null;
135 try {
136 u = new URI( "http://www.deegree.org/app" );
137 } catch ( URISyntaxException e ) {
138 // eat it
139 }
140 APPNS = u;
141 }
142
143 /**
144 * @param baseName
145 * the base name of the file(s), may also end with .mif/.mid
146 * @throws UnsupportedEncodingException
147 * @throws FileNotFoundException
148 */
149 public MapInfoReader( String baseName ) throws FileNotFoundException, UnsupportedEncodingException {
150 if ( baseName.toLowerCase().endsWith( ".mid" ) || baseName.toLowerCase().endsWith( ".mif" ) ) {
151 baseName = baseName.substring( 0, baseName.length() - 4 );
152 LOG.logDebug( "Reading base name of ", baseName );
153 }
154
155 midFile = new File( baseName + ".mid" );
156 if ( !midFile.exists() ) {
157 midFile = new File( baseName + ".MID" );
158 }
159 mifFile = new File( baseName + ".mif" );
160 if ( !mifFile.exists() ) {
161 mifFile = new File( baseName + ".MIF" );
162 }
163
164 mif = getMIFTokenizer( mifFile, null );
165 mid = getMIDTokenizer( midFile, null );
166 }
167
168 /**
169 * Was that so hard?
170 *
171 * @param chars
172 * @param tok
173 */
174 public static void wordChars( StreamTokenizer tok, char... chars ) {
175 for ( char c : chars ) {
176 tok.wordChars( c, c );
177 }
178 }
179
180 /**
181 * Was that so hard?
182 *
183 * @param chars
184 * @param tok
185 */
186 public static void quoteChars( StreamTokenizer tok, char... chars ) {
187 for ( char c : chars ) {
188 tok.quoteChar( c );
189 }
190 }
191
192 /**
193 * Was that so hard?
194 *
195 * @param chars
196 * @param tok
197 */
198 public static void whitespaceChars( StreamTokenizer tok, char... chars ) {
199 for ( char c : chars ) {
200 tok.whitespaceChars( c, c );
201 }
202 }
203
204 // applies heuristics to work around "try what your mapinfo says" in the documentation
205 private static Charset getCharset( String charset ) {
206 if ( charset == null ) {
207 charset = defaultCharset().name();
208 LOG.logDebug( "Parsing with default charset " + charset + " until charset directive is found." );
209 } else if ( charset.equals( "WindowsLatin1" ) ) {
210 LOG.logDebug( "Parsing with charset " + charset + ", interpreting it as iso-8859-1." );
211 charset = "iso-8859-1";
212 } else {
213 LOG.logDebug( "Parsing with unknown charset " + charset + ", hoping Java knows the name. " );
214 }
215
216 return forName( charset );
217 }
218
219 /**
220 * @param file
221 * @param charset
222 * @return a stream tokenizer with some specific settings for reading mif files
223 * @throws FileNotFoundException
224 * @throws UnsupportedEncodingException
225 */
226 public static StreamTokenizer getMIFTokenizer( File file, String charset )
227 throws FileNotFoundException, UnsupportedEncodingException {
228 charset = getCharset( charset ).name();
229
230 StreamTokenizer tok = new StreamTokenizer( new InputStreamReader( new FileInputStream( file ), charset ) );
231
232 tok.resetSyntax();
233 tok.eolIsSignificant( false );
234 tok.lowerCaseMode( true );
235 tok.slashSlashComments( false );
236 tok.slashStarComments( false );
237 tok.wordChars( 'a', 'z' );
238 tok.wordChars( 'A', 'Z' );
239 tok.wordChars( '\u00a0', '\u00ff' );
240 tok.wordChars( '0', '9' );
241 wordChars( tok, '.', '-', '_' );
242 quoteChars( tok, '\'', '"' );
243 whitespaceChars( tok, ' ', '\n', '\r', '\f', '\t', '(', ')', ',' );
244
245 return tok;
246 }
247
248 /**
249 * @param file
250 * @param charset
251 * @return a stream tokenizer for reading MIF files
252 * @throws FileNotFoundException
253 * @throws UnsupportedEncodingException
254 */
255 public static StreamTokenizer getMIDTokenizer( File file, String charset )
256 throws FileNotFoundException, UnsupportedEncodingException {
257 charset = getCharset( charset ).name();
258
259 StreamTokenizer tok = new StreamTokenizer( new InputStreamReader( new FileInputStream( file ), charset ) );
260
261 tok.resetSyntax();
262 tok.eolIsSignificant( true );
263 tok.lowerCaseMode( true );
264 tok.slashSlashComments( false );
265 tok.slashStarComments( false );
266 tok.wordChars( 'a', 'z' );
267 tok.wordChars( 'A', 'Z' );
268 tok.wordChars( '\u00a0', '\u00ff' );
269 tok.wordChars( '0', '9' );
270 wordChars( tok, '.', '-', '_' );
271 tok.quoteChar( '"' );
272 whitespaceChars( tok, ' ', '\n', '\r', '\f', '\t', '(', ')', ',' );
273
274 return tok;
275 }
276
277 /**
278 * @param mif
279 * @return a list of tokens, without the comma
280 * @throws IOException
281 */
282 public static LinkedList<String> parseCommaList( StreamTokenizer mif )
283 throws IOException {
284 LinkedList<String> list = new LinkedList<String>();
285
286 mif.ordinaryChar( ',' );
287 mif.nextToken();
288
289 list.add( mif.sval );
290
291 while ( mif.nextToken() == ',' ) {
292 mif.nextToken();
293 list.add( mif.sval );
294 }
295
296 whitespaceChars( mif, ',' );
297
298 return list;
299 }
300
301 /**
302 * @throws IOException
303 */
304 public void parseFeatureType()
305 throws IOException {
306 // don't parse it twice
307 if ( featureTypeParsed ) {
308 return;
309 }
310
311 delimiter = '\t';
312
313 mid.ordinaryChar( delimiter );
314
315 LinkedList<PropertyType> propertyTypes = new LinkedList<PropertyType>();
316 propertyTypes.add( createGeometryPropertyType( new QualifiedName( "app", "geometry", APPNS ), null, 0, 1 ) );
317
318 while ( mif.nextToken() != TT_EOF ) {
319 if ( mif.sval.equals( "version" ) ) {
320 mif.nextToken();
321 LOG.logDebug( "File version is " + mif.sval );
322 if ( mif.sval.compareTo( "650" ) > 0 ) {
323 LOG.logWarning( "Parsing an unknown version of " + mif.sval + "." );
324 }
325 continue;
326 }
327
328 if ( mif.sval.equals( "charset" ) ) {
329 mif.nextToken();
330 String charset = mif.sval;
331 // get new tokenizer, skip everything until the charset and continue
332 mif = getMIFTokenizer( mifFile, charset );
333 mid = getMIDTokenizer( midFile, charset );
334 mid.ordinaryChar( delimiter );
335 mif.nextToken();
336 while ( !mif.sval.equals( "charset" ) ) {
337 mif.nextToken();
338 }
339 mif.nextToken();
340 continue;
341 }
342
343 if ( mif.sval.equals( "delimiter" ) ) {
344 mif.nextToken();
345 delimiter = mif.sval.charAt( 0 );
346 mid.ordinaryChar( delimiter );
347 continue;
348 }
349
350 if ( mif.sval.equals( "unique" ) ) {
351 mif.nextToken();
352 LOG.logWarning( "Ignoring unique directive." );
353 continue;
354 }
355
356 if ( mif.sval.equals( "index" ) ) {
357 LOG.logWarning( "Ignoring but parsing index directive." );
358 mif.nextToken();
359 while ( true ) {
360 try {
361 parseInt( mif.sval );
362 mif.nextToken();
363 } catch ( NumberFormatException e ) {
364 mif.pushBack();
365 break;
366 }
367 }
368
369 continue;
370 }
371
372 if ( mif.sval.equals( "coordsys" ) ) {
373 LOG.logWarning( "Ignoring coordsys directive." );
374 mif.nextToken();
375 if ( mif.sval.equals( "window" ) ) {
376 mif.nextToken(); // window_id
377 continue;
378 }
379
380 if ( mif.sval.equals( "table" ) ) {
381 mif.nextToken(); // tablename
382 continue;
383 }
384
385 if ( mif.sval.equals( "layout" ) ) {
386 mif.nextToken(); // Units
387 mif.nextToken(); // paperunitname
388 continue;
389 }
390
391 if ( mif.sval.equals( "nonearth" ) ) {
392 mif.nextToken(); // Units or Affine
393 if ( mif.sval.equals( "affine" ) ) {
394 mif.nextToken(); // Units
395 mif.nextToken(); // unitname
396 mif.nextToken(); // A
397 mif.nextToken(); // B
398 mif.nextToken(); // C
399 mif.nextToken(); // D
400 mif.nextToken(); // E
401 mif.nextToken(); // F
402
403 mif.nextToken(); // Units
404 }
405 mif.nextToken(); // unitname
406 mif.nextToken(); // Bounds
407 mif.nextToken(); // minx
408 mif.nextToken(); // miny
409 mif.nextToken(); // maxx
410 mif.nextToken(); // maxy
411 continue;
412 }
413
414 if ( mif.sval.equals( "earth" ) ) {
415 mif.nextToken();
416 if ( mif.sval.equals( "projection" ) ) {
417 parseCommaList( mif );
418 }
419
420 if ( !mif.sval.equals( "affine" ) && !mif.sval.equals( "bounds" ) ) {
421 mif.pushBack();
422 }
423
424 if ( mif.sval.equals( "affine" ) ) {
425 mif.nextToken(); // Units
426 mif.nextToken(); // unitname
427 mif.nextToken(); // A
428 mif.nextToken(); // B
429 mif.nextToken(); // C
430 mif.nextToken(); // D
431 mif.nextToken(); // E
432 mif.nextToken(); // F
433 }
434
435 if ( mif.sval.equals( "bounds" ) ) {
436 mif.nextToken(); // minx
437 mif.nextToken(); // miny
438 mif.nextToken(); // maxx
439 mif.nextToken(); // maxy
440 }
441
442 continue;
443 }
444 }
445
446 if ( mif.sval.equals( "transform" ) ) {
447 mif.nextToken(); // four transformation parameters
448 mif.nextToken();
449 mif.nextToken();
450 mif.nextToken();
451 }
452
453 if ( mif.sval.equals( "columns" ) ) {
454 mif.nextToken();
455 int cnt = parseInt( mif.sval );
456 for ( int i = 0; i < cnt; ++i ) {
457 mif.lowerCaseMode( false );
458 mif.nextToken();
459 String name = mif.sval;
460 mif.lowerCaseMode( true );
461 mif.nextToken();
462 String type = mif.sval;
463 if ( type.equals( "integer" ) ) {
464 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", name, APPNS ), INTEGER,
465 true ) );
466 }
467 if ( type.equals( "smallint" ) ) {
468 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", name, APPNS ), INTEGER,
469 true ) );
470 }
471 if ( type.equals( "float" ) ) {
472 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", name, APPNS ), FLOAT,
473 true ) );
474 }
475 if ( type.equals( "date" ) ) {
476 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", name, APPNS ), DATE,
477 true ) );
478 }
479 if ( type.equals( "logical" ) ) {
480 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", name, APPNS ), BOOLEAN,
481 true ) );
482 }
483 if ( type.equals( "char" ) ) {
484 mif.nextToken(); // size, is ignored (using varchar)
485 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", name, APPNS ), VARCHAR,
486 true ) );
487 }
488 if ( type.equals( "decimal" ) ) {
489 // specifications are ignored, just using double
490 mif.nextToken(); // width
491 mif.nextToken(); // decimals
492 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", name, APPNS ), DOUBLE,
493 true ) );
494 }
495 }
496 continue;
497 }
498
499 if ( mif.sval.equals( "data" ) ) {
500 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", "styleid", APPNS ), VARCHAR,
501 true ) );
502 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", "text_geometry", APPNS ),
503 VARCHAR, true ) );
504 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", "text_minx", APPNS ), VARCHAR,
505 true ) );
506 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", "text_miny", APPNS ), VARCHAR,
507 true ) );
508 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", "text_maxx", APPNS ), VARCHAR,
509 true ) );
510 propertyTypes.add( createSimplePropertyType( new QualifiedName( "app", "text_maxy", APPNS ), VARCHAR,
511 true ) );
512
513 featureType = createFeatureType( new QualifiedName( "app", "someName", APPNS ), false,
514 propertyTypes.toArray( new PropertyType[propertyTypes.size()] ) );
515
516 featureTypeParsed = true;
517 return;
518 }
519
520 LOG.logWarning( "Spurious token: " + mif.sval );
521
522 }
523 }
524
525 /**
526 * @throws IOException
527 */
528 public void parseFeatures()
529 throws IOException {
530 if ( featuresParsed ) {
531 return;
532 }
533
534 parseFeatureType();
535
536 mif.pushBack();
537 while ( mif.nextToken() != TT_EOF ) {
538
539 if ( mif.sval.equals( "data" ) ) {
540 mif.nextToken();
541
542 getFeatures();
543
544 featureCollection = createFeatureCollection( "parsedFeatureCollection",
545 features.toArray( new Feature[features.size()] ) );
546 featuresParsed = true;
547 }
548
549 LOG.logWarning( "Spurious token: " + mif.sval );
550
551 }
552
553 }
554
555 private Pair<FeatureProperty, HashMap<String, HashMap<String, String>>> parseGeometry( CoordinateSystem crs,
556 QualifiedName name )
557 throws IOException {
558 if ( mif.sval.equals( "none" ) ) {
559 mif.nextToken();
560 LOG.logWarning( "A null geometry was found." );
561 return null;
562 }
563
564 HashMap<String, HashMap<String, String>> map = new HashMap<String, HashMap<String, String>>();
565 Pair<FeatureProperty, HashMap<String, HashMap<String, String>>> pair;
566 pair = new Pair<FeatureProperty, HashMap<String, HashMap<String, String>>>();
567 pair.second = map;
568
569 if ( mif.sval.equals( "point" ) ) {
570 Point p = parser.parsePoint();
571
572 HashMap<String, String> symbol = styleParser.parseSymbol();
573 if ( symbol != null ) {
574 map.put( "symbol", symbol );
575 }
576
577 pair.first = createFeatureProperty( name, p );
578
579 return pair;
580 }
581
582 if ( mif.sval.equals( "multipoint" ) ) {
583 MultiPoint mp = parser.parseMultipoint();
584
585 HashMap<String, String> symbol = styleParser.parseSymbol();
586 if ( symbol != null ) {
587 map.put( "symbol", symbol );
588 }
589
590 pair.first = createFeatureProperty( name, mp );
591
592 return pair;
593 }
594
595 if ( mif.sval.equals( "line" ) ) {
596 Curve l = parser.parseLine();
597
598 HashMap<String, String> pen = styleParser.parsePen();
599 if ( pen != null ) {
600 map.put( "pen", pen );
601 }
602
603 pair.first = createFeatureProperty( name, l );
604
605 return pair;
606 }
607
608 if ( mif.sval.equals( "pline" ) ) {
609 Curve c = parser.parsePLine();
610
611 HashMap<String, String> pen = styleParser.parsePen();
612 if ( pen != null ) {
613 map.put( "pen", pen );
614 }
615
616 if ( mif.sval != null && mif.sval.equals( "smooth" ) ) {
617 LOG.logWarning( "Smoothing is not supported, since it uses proprietary ad-hoc algorithms." );
618 mif.nextToken();
619 }
620
621 pair.first = createFeatureProperty( name, c );
622
623 return pair;
624 }
625
626 if ( mif.sval.equals( "region" ) ) {
627 MultiSurface ms = parser.parseRegion();
628
629 HashMap<String, String> pen = styleParser.parsePen();
630 if ( pen != null ) {
631 map.put( "pen", pen );
632 }
633
634 HashMap<String, String> brush = styleParser.parseBrush();
635 if ( brush != null ) {
636 map.put( "brush", brush );
637 }
638
639 if ( mif.sval != null && mif.sval.equals( "center" ) ) {
640 LOG.logWarning( "Custom centroid settings are not supported." );
641 mif.nextToken();
642 mif.nextToken();
643 mif.nextToken();
644 }
645
646 pair.first = createFeatureProperty( name, ms );
647
648 return pair;
649 }
650
651 if ( mif.sval.equals( "arc" ) ) {
652 parser.parseArc();
653
654 HashMap<String, String> pen = styleParser.parsePen();
655 if ( pen != null ) {
656 map.put( "pen", pen );
657 }
658
659 return null;
660 }
661
662 if ( mif.sval.equals( "roundrect" ) ) {
663 parser.parseRoundRect();
664
665 HashMap<String, String> pen = styleParser.parsePen();
666 if ( pen != null ) {
667 map.put( "pen", pen );
668 }
669
670 HashMap<String, String> brush = styleParser.parseBrush();
671 if ( brush != null ) {
672 map.put( "brush", brush );
673 }
674
675 return null;
676 }
677
678 if ( mif.sval.equals( "ellipse" ) ) {
679 parser.parseEllipse();
680
681 HashMap<String, String> pen = styleParser.parsePen();
682 if ( pen != null ) {
683 map.put( "pen", pen );
684 }
685
686 HashMap<String, String> brush = styleParser.parseBrush();
687 if ( brush != null ) {
688 map.put( "brush", brush );
689 }
690
691 return null;
692 }
693
694 if ( mif.sval.equals( "rect" ) ) {
695 Surface s = parser.parseRect();
696
697 HashMap<String, String> pen = styleParser.parsePen();
698 if ( pen != null ) {
699 map.put( "pen", pen );
700 }
701
702 HashMap<String, String> brush = styleParser.parseBrush();
703 if ( brush != null ) {
704 map.put( "brush", brush );
705 }
706
707 pair.first = createFeatureProperty( name, s );
708
709 return pair;
710 }
711
712 if ( mif.sval.equals( "collection" ) ) {
713 LOG.logDebug( "Parsing collection..." );
714 LOG.logWarning( "Collections are not understood and will be ignored. This will break the parsing!" );
715 mif.nextToken();
716 return null;
717 }
718
719 if ( mif.sval.equals( "text" ) ) {
720 LOG.logDebug( "Parsing text..." );
721 LOG.logWarning( "Text geometries will be parsed as points." );
722
723 mif.nextToken();
724 String text = mif.sval;
725 mif.nextToken();
726
727 double x1 = parseDouble( mif.sval );
728 mif.nextToken();
729 double y1 = parseDouble( mif.sval );
730 mif.nextToken();
731
732 double x2 = parseDouble( mif.sval );
733 mif.nextToken();
734 double y2 = parseDouble( mif.sval );
735 mif.nextToken();
736
737 HashMap<String, String> style = styleParser.parseText();
738 if ( style == null ) {
739 style = new HashMap<String, String>();
740 }
741
742 style.put( "minx", "" + x1 );
743 style.put( "miny", "" + y1 );
744 style.put( "maxx", "" + x2 );
745 style.put( "maxy", "" + y2 );
746 style.put( "text", text );
747 map.put( "text", style );
748
749 Point p = createPoint( x1, y1, crs );
750
751 pair.first = createFeatureProperty( name, p );
752
753 return pair;
754 }
755
756 LOG.logWarning( "Unknown construct: " + mif.sval );
757 mif.nextToken();
758
759 return null;
760 }
761
762 // mif stream is kept at beginning of next geometry
763 private void getFeatures()
764 throws IOException {
765 CoordinateSystem crs = null; // TODO
766
767 final QualifiedName styleName = new QualifiedName( "app", "styleid", APPNS );
768 final QualifiedName textName = new QualifiedName( "app", "text_geometry", APPNS );
769 final QualifiedName minxName = new QualifiedName( "app", "text_minx", APPNS );
770 final QualifiedName minyName = new QualifiedName( "app", "text_miny", APPNS );
771 final QualifiedName maxxName = new QualifiedName( "app", "text_maxx", APPNS );
772 final QualifiedName maxyName = new QualifiedName( "app", "text_maxy", APPNS );
773 int styleNum = 0;
774
775 int id = 0;
776
777 features = new LinkedList<Feature>();
778
779 DateFormat df = new SimpleDateFormat( "yyyymmdd" );
780
781 parser = new MIFGeometryParser( mif, crs );
782 styleParser = new MIFStyleParser( mif, this.mifFile.getParentFile() );
783
784 styles = new HashMap<String, HashSet<HashMap<String, String>>>();
785
786 while ( mif.ttype != TT_EOF ) {
787 LinkedList<FeatureProperty> properties = new LinkedList<FeatureProperty>();
788
789 PropertyType[] ps = featureType.getProperties();
790 // skip the styleid, text, and minx, miny, maxx, maxy for the text
791 for ( int i = 0; i < ps.length - 6; ++i ) {
792 String field = null;
793 if ( i != 0 ) { // 0 is the geometry
794 StringBuffer sb = new StringBuffer();
795 while ( mid.nextToken() != delimiter && mid.ttype != TT_EOL && mid.ttype != TT_EOF ) {
796 sb.append( mid.sval );
797 }
798 field = sb.toString();
799 }
800
801 switch ( ps[i].getType() ) {
802 case GEOMETRY: {
803 Pair<FeatureProperty, HashMap<String, HashMap<String, String>>> pair;
804 pair = parseGeometry( crs, ps[i].getName() );
805 if ( pair != null && pair.first != null ) {
806 properties.add( pair.first );
807
808 String usedStyle = null;
809
810 // update styles map
811 for ( String key : pair.second.keySet() ) {
812 HashMap<String, String> ss;
813 ss = pair.second.get( key );
814
815 if ( ss != null ) {
816 if ( key.equals( "text" ) ) {
817 String minx = ss.remove( "minx" );
818 String miny = ss.remove( "miny" );
819 String maxx = ss.remove( "maxx" );
820 String maxy = ss.remove( "maxy" );
821 String text = ss.remove( "text" );
822 properties.add( createFeatureProperty( textName, text ) );
823 properties.add( createFeatureProperty( minxName, minx ) );
824 properties.add( createFeatureProperty( minyName, miny ) );
825 properties.add( createFeatureProperty( maxxName, maxx ) );
826 properties.add( createFeatureProperty( maxyName, maxy ) );
827 }
828
829 if ( styles.get( key ) == null ) {
830 HashSet<HashMap<String, String>> set = new HashSet<HashMap<String, String>>();
831 set.add( ss );
832 styles.put( key, set );
833 ss.put( "styleid", "" + styleNum );
834 if ( usedStyle == null ) {
835 usedStyle = "" + styleNum++;
836 } else {
837 usedStyle = usedStyle + "_" + styleNum++;
838 }
839 } else {
840 HashSet<HashMap<String, String>> set = styles.get( key );
841 if ( !set.contains( ss ) ) {
842 // check for same style, but different ID
843 boolean found = false;
844 String styleid = null;
845 for ( HashMap<String, String> m : set ) {
846 HashMap<String, String> woid = new HashMap<String, String>( m );
847 woid.remove( "styleid" );
848 if ( woid.equals( ss ) ) {
849 found = true;
850 styleid = m.get( "styleid" );
851 break;
852 }
853 }
854 if ( !found ) {
855 set.add( ss );
856 ss.put( "styleid", "" + styleNum );
857 if ( usedStyle == null ) {
858 usedStyle = "" + styleNum++;
859 } else {
860 usedStyle = usedStyle + "_" + styleNum++;
861 }
862 } else {
863 if ( usedStyle == null ) {
864 usedStyle = "" + styleid;
865 } else {
866 usedStyle = usedStyle + "_" + styleid;
867 }
868 }
869 }
870 }
871 }
872 }
873
874 if ( usedStyle != null ) {
875 properties.add( createFeatureProperty( styleName, usedStyle ) );
876 }
877 }
878 continue;
879 }
880 case INTEGER: {
881 Integer val = Integer.valueOf( field );
882 properties.add( createFeatureProperty( ps[i].getName(), val ) );
883 continue;
884 }
885 case FLOAT: {
886 Float val = Float.valueOf( field );
887 properties.add( createFeatureProperty( ps[i].getName(), val ) );
888 continue;
889 }
890 case DOUBLE: {
891 Double val = Double.valueOf( field );
892 properties.add( createFeatureProperty( ps[i].getName(), val ) );
893 continue;
894 }
895 case DATE: {
896 Date val = null;
897 try {
898 val = df.parse( field );
899 properties.add( createFeatureProperty( ps[i].getName(), val ) );
900 } catch ( ParseException e ) {
901 // ignore it
902 LOG.logWarning( "A date value could not be parsed." );
903 }
904 continue;
905 }
906 case BOOLEAN: {
907 Boolean val = Boolean.valueOf( field );
908 properties.add( createFeatureProperty( ps[i].getName(), val ) );
909 continue;
910 }
911 case VARCHAR: {
912 String val = field;
913 properties.add( createFeatureProperty( ps[i].getName(), val ) );
914 continue;
915 }
916 }
917 }
918
919 features.add( createFeature( "" + id++, featureType, properties ) );
920
921 }
922
923 }
924
925 /**
926 * @return the feature collection (null if it has not been parsed yet)
927 */
928 public FeatureCollection getFeatureCollection() {
929 return featureCollection;
930 }
931
932 /**
933 * @return the styles (null, if they've not been parsed yet)
934 */
935 public HashMap<String, HashSet<HashMap<String, String>>> getStyles() {
936 return styles;
937 }
938
939 /**
940 * @return the feature type, or null, if it has not been parsed yet
941 */
942 public FeatureType getFeatureType() {
943 return featureType;
944 }
945
946 /**
947 * @return a list of geometry errors
948 */
949 public LinkedList<String> getErrors() {
950 return parser.errors;
951 }
952
953 }