001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/io/mapinfoapi/MapInfoDataSource.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree. 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 http://www.geographie.uni-bonn.de/deegree/ 007 and 008 lat/lon GmbH 009 http://lat-lon.de/ 010 011 Additional copyright notes: 012 This class is based on the MapInfoDataSource class from the GeoTools project. 013 Geotools - OpenSource mapping toolkit 014 (C) 2002, Centre for Computational Geography 015 016 This library is free software; you can redistribute it and/or modify it under 017 the terms of the GNU Lesser General Public License as published by the Free 018 Software Foundation; either version 2.1 of the License, or (at your option) 019 any later version. 020 This library is distributed in the hope that it will be useful, but WITHOUT 021 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 022 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 023 details. 024 You should have received a copy of the GNU Lesser General Public License 025 along with this library; if not, write to the Free Software Foundation, Inc., 026 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 027 028 Contact information: 029 030 lat/lon GmbH 031 Aennchenstr. 19, 53177 Bonn 032 Germany 033 034 Department of Geography, University of Bonn 035 Prof. Dr. Klaus Greve 036 Postfach 1147, 53001 Bonn 037 Germany 038 039 e-mail: info@deegree.org 040 ----------------------------------------------------------------------------*/ 041 package org.deegree.io.mapinfoapi; 042 043 import java.io.BufferedReader; 044 import java.io.File; 045 import java.io.FileNotFoundException; 046 import java.io.FileReader; 047 import java.io.IOException; 048 import java.net.URL; 049 import java.util.ArrayList; 050 import java.util.StringTokenizer; 051 052 import org.deegree.datatypes.QualifiedName; 053 import org.deegree.datatypes.Types; 054 import org.deegree.framework.log.ILogger; 055 import org.deegree.framework.log.LoggerFactory; 056 import org.deegree.framework.util.CharsetUtils; 057 import org.deegree.framework.util.StringTools; 058 import org.deegree.model.feature.Feature; 059 import org.deegree.model.feature.FeatureCollection; 060 import org.deegree.model.feature.FeatureFactory; 061 import org.deegree.model.feature.FeatureProperty; 062 import org.deegree.model.feature.schema.FeatureType; 063 import org.deegree.model.feature.schema.PropertyType; 064 import org.deegree.model.filterencoding.Filter; 065 import org.deegree.model.spatialschema.JTSAdapter; 066 import org.deegree.ogcwebservices.wfs.operation.Query; 067 068 import com.vividsolutions.jts.geom.Coordinate; 069 import com.vividsolutions.jts.geom.Geometry; 070 import com.vividsolutions.jts.geom.GeometryFactory; 071 import com.vividsolutions.jts.geom.Polygon; 072 import com.vividsolutions.jts.geom.TopologyException; 073 074 /** 075 * TODO add documentation here 076 * 077 * 078 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 079 * @author last edited by: $Author: mschneider $ 080 * 081 * @version $Revision: 18191 $, $Date: 2009-06-18 17:26:44 +0200 (Do, 18 Jun 2009) $ 082 */ 083 public class MapInfoDataSource { 084 085 private static ILogger LOG = LoggerFactory.getLogger( MapInfoDataSource.class ); 086 087 public static final String TYPE_NONE = "none"; 088 089 public static final String TYPE_POINT = "point"; 090 091 public static final String TYPE_LINE = "line"; 092 093 public static final String TYPE_PLINE = "pline"; 094 095 public static final String TYPE_REGION = "region"; 096 097 public static final String TYPE_ARC = "arc"; 098 099 public static final String TYPE_TEXT = "text"; 100 101 public static final String TYPE_RECT = "rectangle"; 102 103 public static final String TYPE_ROUNDRECT = "rounded rectangle"; 104 105 public static final String TYPE_ELLIPSE = "ellipse"; 106 107 public static final String CLAUSE_SYMBOL = "SYMBOL"; 108 109 public static final String CLAUSE_PEN = "PEN"; 110 111 public static final String CLAUSE_SMOOTH = "SMOOTH"; 112 113 public static final String CLAUSE_CENTER = "CENTER"; 114 115 public static final String CLAUSE_BRUSH = "BRUSH"; 116 117 public static final String CLAUSE_VERSION = "Version"; 118 119 public static final String CLAUSE_CHARSET = "Charset"; 120 121 public static final String CLAUSE_DELIMETER = "DELIMITER"; 122 123 public static final String CLAUSE_UNIQUE = "UNIQUE"; 124 125 public static final String CLAUSE_INDEX = "INDEX"; 126 127 public static final String CLAUSE_COLUMNS = "COLUMNS"; 128 129 private ArrayList<String> hColumnsNames; 130 131 private ArrayList<String> hColumnsTypes; 132 133 private ArrayList<String> hIndex; 134 135 private ArrayList<String> hUnique; 136 137 private FeatureType lineType = null; 138 139 private FeatureType pointType = null; 140 141 private FeatureType polygonType = null; 142 143 // Factory to use to build Geometries 144 private GeometryFactory geomFactory; 145 146 private String filename; 147 148 private String hCharset; 149 150 private String hDelimeter = "\t"; 151 152 // Header information 153 private String hVersion; 154 155 // CoordsSys not supported 156 // Transform not supported 157 // Global variables (for the initial read) 158 private String line; // The current Line of the MIF file. 159 160 /** 161 * Creates a new MapInfoDataSource object. 162 * 163 * @param url 164 * 165 * @throws java.net.MalformedURLException 166 */ 167 public MapInfoDataSource( URL url ) throws java.net.MalformedURLException { 168 try { 169 filename = java.net.URLDecoder.decode( url.getFile(), CharsetUtils.getSystemCharset() ); 170 } catch ( Exception e ) { 171 throw new java.net.MalformedURLException( e.toString() ); 172 } 173 geomFactory = new GeometryFactory(); 174 } 175 176 /** 177 * Reads the MIF and MID files and returns a ArrayList of the Features they contain 178 * 179 * @return a ArrayList of features? 180 * 181 * @throws Exception 182 * if file doesn't exist or is not readable etc 183 */ 184 protected ArrayList readMifMid() 185 throws Exception { 186 if ( filename == null ) { 187 throw new Exception( "Invalid filename passed to readMifMid" ); 188 } 189 190 String mifFile = setExtension( filename, "MIF" ); 191 String midFile = setExtension( filename, "MID" ); 192 193 // Read files 194 try { 195 File mif = new File( mifFile ); 196 197 if ( !mif.exists() ) { 198 mifFile = setExtension( filename, "mif" ); 199 mif = new File( mifFile ); 200 201 if ( !mif.exists() ) { 202 mifFile = setExtension( filename.toLowerCase(), "mif" ); 203 mif = new File( mifFile ); 204 205 if ( !mif.exists() ) { 206 mifFile = setExtension( filename.toUpperCase(), "MIF" ); 207 mif = new File( mifFile ); 208 } // and at that I'm out of guesses 209 } 210 } 211 212 File mid = new File( midFile ); 213 214 if ( !mid.exists() ) { 215 midFile = setExtension( filename, "mid" ); 216 mid = new File( midFile ); 217 218 if ( !mid.exists() ) { 219 midFile = setExtension( filename.toLowerCase(), "mid" ); 220 mid = new File( midFile ); 221 222 if ( !mid.exists() ) { 223 midFile = setExtension( filename.toUpperCase(), "MID" ); 224 mid = new File( midFile ); 225 } // and at that I'm out of guesses 226 } 227 } 228 229 ArrayList features = readMifMid( new BufferedReader( new FileReader( mif ) ), 230 new BufferedReader( new FileReader( mid ) ) ); 231 232 return features; 233 } catch ( FileNotFoundException fnfexp ) { 234 throw new Exception( "FileNotFoundException trying to read mif file : ", fnfexp ); 235 } 236 } 237 238 /** 239 * @param filename 240 * @param ext 241 * 242 * @return 243 */ 244 private String setExtension( String filename, String ext ) { 245 if ( ext.indexOf( "." ) == -1 ) { 246 ext = "." + ext; 247 } 248 249 if ( filename.lastIndexOf( "." ) == -1 ) { 250 return filename + ext; 251 } 252 253 return filename.substring( 0, filename.lastIndexOf( "." ) ) + ext; 254 } 255 256 /** 257 * This private method constructs the factories used to create the Feature, and Geometries as 258 * they are read It takes it's setup values from the value of the COLUMNS clause in the MIF file 259 * 260 * @throws Exception 261 */ 262 private void setUpFactories() 263 throws Exception { 264 // Go through each column name, and set up an attribute for each one 265 ArrayList<PropertyType> colAttribs = new ArrayList<PropertyType>( hColumnsNames.size() ); 266 267 // Add attributes for each column 268 // Iterator it = hColumns.keySet().iterator(); 269 for ( int i = 0; i < hColumnsNames.size(); i++ ) { 270 String type = hColumnsTypes.get( i ).toLowerCase(); 271 int typeClass = -999; 272 273 if ( type.equals( "float" ) || type.startsWith( "decimal" ) ) { 274 typeClass = Types.NUMERIC; 275 hColumnsTypes.set( i, "Double" ); 276 } else if ( type.startsWith( "char" ) ) { 277 typeClass = Types.VARCHAR; 278 hColumnsTypes.set( i, "String" ); 279 } else if ( type.equals( "integer" ) || type.equals( "smallint" ) ) { 280 typeClass = Types.INTEGER; 281 hColumnsTypes.set( i, "Integer" ); 282 } else { 283 typeClass = Types.VARCHAR; 284 hColumnsTypes.set( i, "String" ); 285 } 286 287 PropertyType ftp = FeatureFactory.createSimplePropertyType( new QualifiedName( hColumnsNames.get( i ) ), 288 typeClass, true ); 289 colAttribs.add( ftp ); 290 } 291 292 PropertyType ftp = FeatureFactory.createSimplePropertyType( new QualifiedName( "GEOM" ), Types.GEOMETRY, true ); 293 294 // Add default Geometry attribute type 295 colAttribs.add( 0, ftp ); 296 297 // create point feature Type & factory 298 try { 299 PropertyType[] ftps = colAttribs.toArray( new PropertyType[colAttribs.size()] ); 300 pointType = FeatureFactory.createFeatureType( filename.toString() + "_point", false, ftps ); 301 } catch ( Exception schexp ) { 302 throw new Exception( "SchemaException setting up point factory : ", schexp ); 303 } 304 305 // Set up Line factory 306 // Add default attribute type 307 ftp = FeatureFactory.createSimplePropertyType( new QualifiedName( "GEOM" ), Types.GEOMETRY, true ); 308 colAttribs.set( 0, ftp ); 309 310 // create line feature Type & factory 311 try { 312 PropertyType[] ftps = colAttribs.toArray( new PropertyType[colAttribs.size()] ); 313 lineType = FeatureFactory.createFeatureType( filename.toString() + "_line", false, ftps ); 314 } catch ( Exception schexp ) { 315 throw new Exception( "SchemaException setting up line factory : ", schexp ); 316 } 317 318 // Set up Polygon factory 319 // Add default attribute type 320 ftp = FeatureFactory.createSimplePropertyType( new QualifiedName( "GEOM" ), Types.GEOMETRY, true ); 321 colAttribs.set( 0, ftp ); 322 323 // create polygon feature Type & factory 324 try { 325 PropertyType[] ftps = colAttribs.toArray( new PropertyType[colAttribs.size()] ); 326 polygonType = FeatureFactory.createFeatureType( filename.toString() + "_poly", false, ftps ); 327 } catch ( Exception schexp ) { 328 throw new Exception( "SchemaException setting up polygon factory : ", schexp ); 329 } 330 } 331 332 /** 333 * Reads an entire MID/MIF file. (Two files, actually, separately opened) 334 * 335 * @param mifReader 336 * An opened BufferedReader to the MIF file. 337 * @param midReader 338 * An opened BufferedReader to the MID file. 339 * 340 * @return 341 * @throws Exception 342 */ 343 private ArrayList readMifMid( BufferedReader mifReader, BufferedReader midReader ) 344 throws Exception { 345 // Read the MIF header 346 readMifHeader( mifReader ); 347 348 // Set up factories 349 setUpFactories(); 350 351 ArrayList<Feature> features = new ArrayList<Feature>( 100 ); 352 353 // Start by reading first line 354 try { 355 line = readMifLine( mifReader ); 356 } catch ( IOException ioexp ) { 357 throw new Exception( "No data at start of file", ioexp ); 358 } 359 360 Feature feature; 361 362 // Read each object in the MIF file 363 while ( ( feature = readObject( mifReader, midReader ) ) != null ) { 364 // Figure out which type of feature it is 365 // Add to relevent ArrayList 366 features.add( feature ); 367 } 368 369 return features; 370 } 371 372 /** 373 * Reads the header from the given MIF file stream 374 * 375 * @param mifReader 376 * 377 * @throws Exception 378 */ 379 private void readMifHeader( BufferedReader mifReader ) 380 throws Exception { 381 try { 382 while ( ( readMifLine( mifReader ) != null ) && !line.trim().equalsIgnoreCase( "DATA" ) ) { 383 if ( clause( line ).equalsIgnoreCase( CLAUSE_VERSION ) ) { 384 // Read Version clause 385 hVersion = line.trim().substring( line.trim().indexOf( ' ' ) ).trim(); 386 LOG.logDebug( "version [" + hVersion + "]" ); 387 } 388 389 if ( clause( line ).equalsIgnoreCase( CLAUSE_CHARSET ) ) { 390 // Read Charset clause 391 // hCharset = line.replace('\"',' 392 // ').trim().substring(line.trim().indexOf(' ')).trim(); 393 hCharset = remainder( line ).replace( '"', ' ' ).trim(); 394 LOG.logDebug( "Charset [" + hCharset + "]" ); 395 } 396 397 if ( clause( line ).equalsIgnoreCase( CLAUSE_DELIMETER ) ) { 398 // Read Delimeter clause 399 hDelimeter = line.replace( '\"', ' ' ).trim().substring( line.trim().indexOf( ' ' ) ).trim(); 400 LOG.logDebug( "delimiter [" + hDelimeter + "]" ); 401 } 402 403 if ( clause( line ).equalsIgnoreCase( CLAUSE_UNIQUE ) ) { 404 // Read Unique clause 405 StringTokenizer st = new StringTokenizer( line.trim().substring( line.trim().indexOf( ' ' ) ), "," ); 406 hUnique = new ArrayList<String>(); 407 LOG.logDebug( "Unique cols " ); 408 409 while ( st.hasMoreTokens() ) { 410 String uniq = st.nextToken(); 411 LOG.logDebug( "\t" + uniq ); 412 hUnique.add( uniq ); 413 } 414 } 415 416 if ( clause( line ).equalsIgnoreCase( CLAUSE_INDEX ) ) { 417 // Read Index clause 418 StringTokenizer st = new StringTokenizer( line.trim().substring( line.trim().indexOf( ' ' ) ), "," ); 419 hIndex = new ArrayList<String>( st.countTokens() ); 420 LOG.logDebug( "Indexes" ); 421 422 while ( st.hasMoreTokens() ) { 423 String index = st.nextToken(); 424 LOG.logDebug( "\t" + index ); 425 hIndex.add( index ); 426 } 427 } 428 429 if ( clause( line ).equalsIgnoreCase( CLAUSE_COLUMNS ) ) { 430 // Read Columns clause 431 int cols = 0; 432 433 try { 434 cols = Integer.parseInt( remainder( line ) ); 435 } catch ( NumberFormatException nfexp ) { 436 LOG.logError( "bad number of colums ", nfexp ); 437 } 438 439 // Read each of the columns 440 hColumnsNames = new ArrayList<String>( cols ); 441 hColumnsTypes = new ArrayList<String>( cols ); 442 443 for ( int i = 0; i < cols; i++ ) { 444 line = readMifLine( mifReader ); 445 446 // StringTokenizer st = new 447 // StringTokenizer(line.trim().substring(line.trim().indexOf(' 448 // ')), " "); 449 String name = clause( line ); 450 String value = remainder( line ); 451 452 LOG.logDebug( "column name " + name + " value " + value ); 453 454 hColumnsNames.add( name ); 455 hColumnsTypes.add( value ); 456 } 457 } 458 } 459 } catch ( IOException ioexp ) { 460 throw new Exception( "IOException reading MIF header : " + ioexp.getMessage() ); 461 } 462 } 463 464 /** 465 * A 'Clause' is stored as a single string at the start of a line. This rips the clause name out 466 * of the given line. 467 * 468 * @param line 469 * 470 * @return 471 */ 472 private String clause( String line ) { 473 return clause( line, ' ' ); 474 } 475 476 /** 477 * @param line 478 * @param delimiter 479 * 480 * @return 481 */ 482 private String clause( String line, char delimiter ) { 483 line = line.trim(); 484 485 int index = line.indexOf( delimiter ); 486 487 if ( index == -1 ) { 488 return line; 489 } 490 return line.substring( 0, index ).trim(); 491 } 492 493 /** 494 * returns the last word of the string 495 * 496 * @param line 497 * 498 * @return the last word of the string 499 */ 500 private String remainder( String line ) { 501 return remainder( line, ' ' ); 502 } 503 504 /** 505 * @param line 506 * @param delimiter 507 * 508 * @return 509 */ 510 private String remainder( String line, char delimiter ) { 511 line = line.trim(); 512 513 int index = line.lastIndexOf( delimiter ); 514 515 if ( index == -1 ) { 516 return ""; 517 } 518 return line.substring( index ).trim(); 519 } 520 521 /** 522 * Reads the next line in the reader, ignoring lines which are nothing but whitespace. Sets the 523 * global 'line' variable to the currently read line 524 * 525 * @param reader 526 * 527 * @return 528 * @throws IOException 529 * @throws Exception 530 */ 531 private String readMifLine( BufferedReader reader ) 532 throws IOException, Exception { 533 do { 534 line = reader.readLine(); 535 536 if ( line == null ) { 537 return null; 538 } 539 540 if ( isShadingClause( line ) ) { 541 LOG.logDebug( "going to process shading" ); 542 543 // processShading(line); 544 line = " "; 545 } 546 } while ( line.trim().length() == 0 ); 547 548 line = line.trim(); 549 550 // LOG.logDebug("returning line " + line); 551 return line; 552 } 553 554 /** 555 * Reads a single MIF Object (Point, Line, Region, etc.) as a Feature 556 * 557 * @param mifReader 558 * @param midReader 559 * 560 * @return 561 * @throws Exception 562 */ 563 private Feature readObject( BufferedReader mifReader, BufferedReader midReader ) 564 throws Exception { 565 Feature feature = null; 566 567 // LOG.logDebug("line = " + line); 568 // examine The current line 569 if ( line == null ) { 570 return null; 571 } 572 573 int index = line.indexOf( ' ' ); 574 575 if ( index == -1 ) { 576 index = line.length(); 577 } 578 579 if ( line.substring( 0, index ).equalsIgnoreCase( TYPE_POINT ) ) { 580 // Read point data 581 feature = readPointObject( mifReader, midReader ); 582 } else if ( line.substring( 0, index ).equalsIgnoreCase( TYPE_LINE ) ) { 583 // Read line data 584 feature = readLineObject( mifReader, midReader ); 585 } else if ( line.substring( 0, index ).equalsIgnoreCase( TYPE_PLINE ) ) { 586 // Read pline data 587 feature = readPLineObject( mifReader, midReader ); 588 } else if ( line.substring( 0, index ).equalsIgnoreCase( TYPE_REGION ) ) { 589 // Read region data 590 feature = readRegionObject( mifReader, midReader ); 591 } else { 592 LOG.logDebug( line + " unknown object in mif reader" ); 593 } 594 595 return feature; 596 } 597 598 /** 599 * Reads Point information from the MIF stream 600 * 601 * @param mifReader 602 * @param midReader 603 * 604 * @return 605 * @throws Exception 606 */ 607 private Feature readPointObject( BufferedReader mifReader, BufferedReader midReader ) 608 throws Exception { 609 Feature feature = null; 610 611 StringTokenizer st = new StringTokenizer( line.substring( line.indexOf( " " ) ), "," ); 612 613 try { 614 double x = Double.parseDouble( st.nextToken() ); 615 double y = Double.parseDouble( st.nextToken() ); 616 617 // Construct Geomtry 618 Geometry pointGeom = geomFactory.createPoint( new Coordinate( x, y ) ); 619 620 // Read next line 621 readMifLine( mifReader ); 622 623 // Hashtable shading = readShading(mifReader); 624 // Shading is not included, as null feature attributes are not 625 // supported yet 626 ArrayList<Object> midValues = readMid( midReader ); 627 628 // midValues.putAll(shading); 629 // Create Feature 630 feature = buildFeature( pointType, pointGeom, midValues ); 631 632 LOG.logDebug( "Built point feature : " + x + " " + y ); 633 } catch ( NumberFormatException nfexp ) { 634 throw new Exception( "Exception reading Point data from MIF file : ", nfexp ); 635 } catch ( IOException ioexp ) { 636 throw new Exception( "IOException reading point data : ", ioexp ); 637 } 638 639 return feature; 640 } 641 642 /** 643 * Reads Line information from the MIF stream 644 * 645 * @param mifReader 646 * @param midReader 647 * 648 * @return 649 * @throws Exception 650 */ 651 private Feature readLineObject( BufferedReader mifReader, BufferedReader midReader ) 652 throws Exception { 653 Feature feature = null; 654 655 StringTokenizer st = new StringTokenizer( line.substring( line.indexOf( " " ) ), "," ); 656 657 try { 658 double x1 = Double.parseDouble( st.nextToken() ); 659 double y1 = Double.parseDouble( st.nextToken() ); 660 double x2 = Double.parseDouble( st.nextToken() ); 661 double y2 = Double.parseDouble( st.nextToken() ); 662 663 // Construct Geomtry 664 Coordinate[] cPoints = new Coordinate[2]; 665 cPoints[0] = new Coordinate( x1, y1 ); 666 cPoints[1] = new Coordinate( x2, y2 ); 667 668 Geometry lineGeom = geomFactory.createLineString( cPoints ); 669 670 // Read next line 671 readMifLine( mifReader ); 672 673 // Hashtable shading = readShading(mifReader); 674 // Shading is not included, as null feature attributes are not 675 // supported yet 676 ArrayList<Object> midValues = readMid( midReader ); 677 678 // midValues.putAll(shading); 679 // Create Feature 680 feature = buildFeature( lineType, lineGeom, midValues ); 681 682 LOG.logDebug( "Built line feature : " + x1 + " " + y1 + " - " + x2 + " " + y2 ); 683 } catch ( NumberFormatException nfexp ) { 684 throw new Exception( "Exception reading Point data from MIF file : " + nfexp.getMessage() ); 685 } catch ( IOException ioexp ) { 686 throw new Exception( "IOException reading point data : " + ioexp.getMessage() ); 687 } 688 689 return feature; 690 } 691 692 /** 693 * Reads Multi-Line (PLine) information from the MIF stream 694 * 695 * @param mifReader 696 * @param midReader 697 * 698 * @return 699 * @throws Exception 700 */ 701 private Feature readPLineObject( BufferedReader mifReader, BufferedReader midReader ) 702 throws Exception { 703 Feature feature = null; 704 705 StringTokenizer st = new StringTokenizer( line.substring( line.indexOf( " " ) ) ); 706 707 try { 708 int numsections = 1; 709 710 if ( st.hasMoreTokens() && st.nextToken().trim().equalsIgnoreCase( "MULTIPLE" ) ) { 711 numsections = Integer.parseInt( st.nextToken() ); 712 } 713 714 // A ArrayList of coordinates 715 ArrayList<Coordinate> coords = new ArrayList<Coordinate>( numsections ); 716 717 // Read each polygon 718 for ( int i = 0; i < numsections; i++ ) { 719 // Read line (number of points 720 int numpoints = Integer.parseInt( readMifLine( mifReader ) ); 721 722 // Read each point 723 for ( int p = 0; p < numpoints; p++ ) { 724 StringTokenizer pst = new StringTokenizer( readMifLine( mifReader ) ); 725 double x = Double.parseDouble( pst.nextToken() ); 726 double y = Double.parseDouble( pst.nextToken() ); 727 coords.add( new Coordinate( x, y ) ); 728 } 729 } 730 731 Geometry plineGeom = geomFactory.createLineString( coords.toArray( new Coordinate[coords.size()] ) ); 732 733 // Read next line 734 readMifLine( mifReader ); 735 736 // Hashtable shading = readShading(mifReader); 737 // Shading is not included, as null feature attributes are not 738 // supported yet 739 ArrayList<Object> midValues = readMid( midReader ); 740 741 // midValues.putAll(shading); 742 // Create Feature 743 feature = buildFeature( lineType, plineGeom, midValues ); 744 745 LOG.logDebug( "Read polyline (" + coords.size() + ")" ); 746 } catch ( NumberFormatException nfexp ) { 747 throw new Exception( "Exception reading Point data from MIF file : " + nfexp.getMessage() ); 748 } catch ( IOException ioexp ) { 749 throw new Exception( "IOException reading point data : " + ioexp.getMessage() ); 750 } 751 752 return feature; 753 } 754 755 /** 756 * Reads Region (Polygon) information from the MIF stream 757 * 758 * @param mifReader 759 * @param midReader 760 * 761 * @return 762 * @throws Exception 763 */ 764 private Feature readRegionObject( BufferedReader mifReader, BufferedReader midReader ) 765 throws Exception { 766 Feature feature = null; 767 768 StringTokenizer st = new StringTokenizer( line.substring( line.indexOf( " " ) ) ); 769 770 try { 771 int numpolygons = Integer.parseInt( st.nextToken() ); 772 773 // A ArrayList of polygons 774 ArrayList<Polygon> polys = new ArrayList<Polygon>( numpolygons ); 775 776 // Read each polygon 777 for ( int i = 0; i < numpolygons; i++ ) { 778 // Read number of points 779 int numpoints = Integer.parseInt( readMifLine( mifReader ) ); 780 ArrayList<Coordinate> coords = new ArrayList<Coordinate>( numpoints ); 781 782 // Read each point 783 for ( int p = 0; p < numpoints; p++ ) { 784 StringTokenizer pst = new StringTokenizer( readMifLine( mifReader ) ); 785 double x = Double.parseDouble( pst.nextToken() ); 786 double y = Double.parseDouble( pst.nextToken() ); 787 coords.add( new Coordinate( x, y ) ); 788 } 789 790 // Create polygon from points 791 coords.add( new Coordinate( ( coords.get( 0 ) ).x, ( coords.get( 0 ) ).y ) ); 792 793 try { 794 Polygon pol = geomFactory.createPolygon( 795 geomFactory.createLinearRing( coords.toArray( new Coordinate[coords.size()] ) ), 796 null ); 797 798 // Add to ArrayList 799 polys.add( pol ); 800 } catch ( TopologyException topexp ) { 801 throw new Exception( "TopologyException reading Region polygon : ", topexp ); 802 } 803 } 804 805 Geometry polyGeom = geomFactory.createMultiPolygon( polys.toArray( new Polygon[polys.size()] ) ); 806 807 // Read next line 808 readMifLine( mifReader ); 809 810 // Hashtable shading = readShading(mifReader); 811 // Shading is not included, as null feature attributes are not 812 // supported yet 813 ArrayList<Object> midValues = readMid( midReader ); 814 815 // midValues.putAll(shading); 816 // Create Feature 817 feature = buildFeature( polygonType, polyGeom, midValues ); 818 819 LOG.logDebug( "Read Region (" + polys.size() + ")" ); 820 } catch ( NumberFormatException nfexp ) { 821 throw new Exception( "Exception reading Point data from MIF file : ", nfexp ); 822 } catch ( IOException ioexp ) { 823 throw new Exception( "IOException reading point data : ", ioexp ); 824 } 825 826 return feature; 827 } 828 829 /** 830 * Builds a complete Feature object using the given FeatureType, with the Geometry geom, and the 831 * given attributes. 832 * 833 * @param featureType 834 * The FeatureType to use to constuct the Feature 835 * @param geom 836 * The Geometry to use as the default Geometry 837 * @param attribs 838 * The attibutes to use as the Feature's attributes (Attributes must be set up in the 839 * FeatureType) 840 * 841 * @return A fully-formed Feature 842 * 843 * @throws Exception 844 */ 845 private Feature buildFeature( FeatureType featureType, Geometry geom, ArrayList<Object> attribs ) 846 throws Exception { 847 int numAttribs = featureType.getProperties().length; 848 849 // add geometry to the attributes 850 attribs.add( 0, JTSAdapter.wrap( geom ) ); 851 852 if ( numAttribs != attribs.size() ) { 853 throw new Exception( "wrong number of attributes passed to buildFeature.\n" + "expected " + numAttribs 854 + " got " + attribs.size() ); 855 } 856 857 // Create Feature 858 FeatureProperty[] fp = new FeatureProperty[attribs.size()]; 859 PropertyType[] ftp = featureType.getProperties(); 860 for ( int i = 0; i < fp.length; i++ ) { 861 fp[i] = FeatureFactory.createFeatureProperty( ftp[i].getName(), attribs.get( i ) ); 862 } 863 try { 864 return FeatureFactory.createFeature( "id", featureType, fp ); 865 } catch ( Exception ifexp ) { 866 throw new Exception( "Exception creating feature : ", ifexp ); 867 } 868 } 869 870 /** 871 * Reads a single line of the given MID file stream, and returns a hashtable of the data in it, 872 * keyed byt he keys in the hColumns hash 873 * 874 * @param midReader 875 * 876 * @return 877 * @throws Exception 878 */ 879 private ArrayList<Object> readMid( BufferedReader midReader ) 880 throws Exception { 881 ArrayList<Object> midValues = new ArrayList<Object>(); 882 883 if ( midReader == null ) { 884 return midValues; 885 } 886 887 // The delimeter is a single delimiting character 888 String midLine = ""; 889 890 try { 891 midLine = midReader.readLine(); 892 LOG.logDebug( "Read MID " + midLine ); 893 } catch ( IOException ioexp ) { 894 throw new Exception( "IOException reading MID file" ); 895 } 896 897 // read MID tokens 898 int col = 0; 899 StringTokenizer quotes = new StringTokenizer( midLine, "\"" ); 900 901 while ( quotes.hasMoreTokens() ) { 902 StringTokenizer delimeters = new StringTokenizer( quotes.nextToken(), hDelimeter + "\0" ); 903 904 // Read each delimited value into the ArrayList 905 while ( delimeters.hasMoreTokens() ) { 906 String token = delimeters.nextToken(); 907 String type = hColumnsTypes.get( col++ ); 908 addAttribute( type, token, midValues ); 909 } 910 911 // Store the whole of the next bit (it's a quoted string) 912 if ( quotes.hasMoreTokens() ) { 913 String token = quotes.nextToken(); 914 String type = hColumnsTypes.get( col++ ); 915 addAttribute( type, token, midValues ); 916 LOG.logDebug( "adding " + token ); 917 } 918 } 919 920 return midValues; 921 } 922 923 /** 924 * @param type 925 * @param token 926 * @param midValues 927 */ 928 private void addAttribute( String type, String token, ArrayList<Object> midValues ) { 929 if ( type.equals( "String" ) ) { 930 midValues.add( token ); 931 } else if ( type.equals( "Double" ) ) { 932 try { 933 token = StringTools.validateString( token, "," ); 934 midValues.add( new Double( token ) ); 935 } catch ( NumberFormatException nfe ) { 936 LOG.logError( "Bad double " + token, nfe ); 937 midValues.add( new Double( 0.0 ) ); 938 } 939 } else if ( type.equals( "Integer" ) ) { 940 try { 941 // token = StringExtend.validateString( token, "," ); 942 midValues.add( new Integer( token ) ); 943 } catch ( NumberFormatException nfe ) { 944 LOG.logError( "Bad integer " + token, nfe ); 945 midValues.add( new Integer( 0 ) ); 946 } 947 } else { 948 LOG.logDebug( "Unknown type " + type ); 949 } 950 } 951 952 /** 953 * Reads the shading information at the end of Object data 954 * 955 * @param line 956 * 957 * @throws Exception 958 */ 959 960 // private void processShading(String line) throws Exception { 961 // int color; 962 // int r; 963 // int g; 964 // int b; 965 // 966 // if (line == null) { 967 // return; 968 // } 969 // 970 // String shadeType = line.toLowerCase(); 971 // String name = clause(shadeType, '('); 972 // String settings = remainder(shadeType, '('); 973 // StringTokenizer st = new StringTokenizer(settings, "(),"); 974 // String[] values = new String[st.countTokens()]; 975 // 976 // for (int i = 0; st.hasMoreTokens(); i++) { 977 // values[i] = st.nextToken(); 978 // } 979 // 980 // if (name.equals("pen")) { 981 // try { 982 // 983 // stroke.setWidth(filterFactory.createLiteralExpression(new 984 // Integer(values[0]))); 985 // 986 // int pattern = Integer.parseInt(values[1]); 987 // 988 // stroke.setDashArray(MifStyles.getPenPattern(new Integer(pattern))); 989 // color = Integer.parseInt(values[2]); 990 // 991 // String rgb = Integer.toHexString(color); 992 // 993 // stroke.setColor(filterFactory.createLiteralExpression(rgb)); 994 // } catch (Exception nfe) { 995 // throw new Exception("Error setting up pen", nfe); 996 // } 997 // 998 // return; 999 // } else if (name.equals("brush")) { 1000 // LOG.logDebug("setting new brush " + settings); 1001 // 1002 // int pattern = Integer.parseInt(values[0]); 1003 // LOG.logDebug("pattern = " + pattern); 1004 // 1005 // Graphic dg = styleFactory.getDefaultGraphic(); 1006 // dg.addExternalGraphic(MifStyles.getBrushPattern(new Integer(pattern))); 1007 // stroke.setGraphicFill(dg); 1008 // color = Integer.parseInt(values[1]); 1009 // 1010 // String rgb = Integer.toHexString(color); 1011 // LOG.logDebug("color " + color + " -> " + rgb); 1012 // fill.setColor(filterFactory.createLiteralExpression(rgb)); // foreground 1013 // 1014 // if (values.length == 3) { // optional parameter 1015 // color = Integer.parseInt(values[2]); 1016 // rgb = Integer.toHexString(color); 1017 // LOG.logDebug("color " + color + " -> " + rgb); 1018 // 1019 // fill.setBackgroundColor(filterFactory.createLiteralExpression(rgb)); // 1020 // background 1021 // } else { 1022 // fill.setBackgroundColor((Expression) null); 1023 // } 1024 // } else if (name.equals("center")) { 1025 // LOG.logDebug("setting center " + settings); 1026 // } else if (name.equals("smooth")) { 1027 // LOG.logDebug("setting smooth on"); 1028 // } else if (name.equals("symbol")) { 1029 // LOG.logDebug("setting symbol " + settings); 1030 // 1031 // Mark symbol = null; 1032 // ExternalGraphic eg = null; 1033 // 1034 // if (values.length == 3) { // version 3.0 1035 // 1036 // //symbol = symbols.get(new Integer(symNumb)); 1037 // } else if (values.length == 6) {} 1038 // else if (values.length == 4) { // custom bitmap 1039 // eg = styleFactory.createExternalGraphic("CustSymb/" + 1040 // values[0],"image/unknown"); // hack! 1041 // 1042 // } else { 1043 // LOGGER.info("unexpected symbol style " + name + settings); 1044 // } 1045 // } else if (name.equals("font")) { 1046 // LOG.logDebug("setting font " + settings); 1047 // } else { 1048 // LOG.logDebug("unknown styling directive " + name + settings); 1049 // } 1050 // 1051 // return; 1052 // } 1053 /** 1054 * Test whether the given line contains a known shading clause keyword (PEN, STYLE, etc.) 1055 * 1056 * @param line 1057 * 1058 * @return 1059 */ 1060 private boolean isShadingClause( String line ) { 1061 line = line.toUpperCase(); 1062 1063 boolean ret = ( ( line.indexOf( CLAUSE_PEN ) != -1 ) || ( line.indexOf( CLAUSE_SYMBOL ) != -1 ) 1064 || ( line.indexOf( CLAUSE_SMOOTH ) != -1 ) || ( line.indexOf( CLAUSE_CENTER ) != -1 ) || line.indexOf( CLAUSE_BRUSH ) != -1 ); 1065 1066 return ret; 1067 } 1068 1069 /** 1070 * Loads features from the datasource into the passed collection, based on the passed filter. 1071 * Note that all data sources must support this method at a minimum. 1072 * 1073 * @param collection 1074 * The collection to put the features into. 1075 * @param query 1076 * contains info about request of which features to retrieve. 1077 * 1078 * @throws Exception 1079 * For all data source errors. 1080 */ 1081 public void getFeatures( FeatureCollection collection, Query query ) 1082 throws Exception { 1083 Filter filter = null; 1084 1085 if ( query != null ) { 1086 filter = query.getFilter(); 1087 } 1088 1089 ArrayList features = readMifMid(); 1090 1091 for ( int i = 0; i < features.size(); i++ ) { 1092 if ( ( filter == null ) || filter.evaluate( (Feature) features.get( i ) ) ) { 1093 collection.add( (Feature) features.get( i ) ); 1094 } 1095 } 1096 } 1097 1098 /** 1099 * Retrieves the featureType that features extracted from this datasource will be created with. 1100 * TODO: implement this method. 1101 * 1102 * @return <code>null</code> 1103 */ 1104 public FeatureType getSchema() { 1105 return null; 1106 } 1107 }