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 }