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