/* * ShapeReader.java * * Created on June 27, 2002, 2:49 PM */ /* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.io; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jump.I18N; import com.vividsolutions.jump.feature.*; import org.geotools.dbffile.DbfFile; import org.geotools.shapefile.Shapefile; import org.geotools.shapefile.ShapefileException; import java.io.*; import java.nio.charset.Charset; /** * ShapefileReader is a {@link JUMPReader} specialized to read Shapefiles. * * <p> * DataProperties for the JUMPReader load(DataProperties) interface:<br><br> * </p> * * <p> * <table border='1' cellspacing='0' cellpadding='4'> * <tr> * <th>Parameter</th><th>Meaning</th> * </tr> * <tr> * <td>InputFile or DefaultValue</td> * <td>File name for the input .shp file</td> * </tr> * <tr> * <td colspan='2'> * NOTE: The input .dbf is assumed to be 'beside' (in the same * directory) as the .shp file. * </td> * </tr> * <tr> * <td>CompressedFile</td> * <td>File name (.zip NOT a .gz) with a .shp and .dbf file inside</td> * </tr> * * <tr> * <td colspan='2'> * Uses a modified version of geotools to do the .dbf and .shp * file reading. If you are reading from a .zip file, the dbf * file will be copied to your temp directory and deleted * after being read. * </td> * </tr> * </table> */ public class ShapefileReader implements JUMPReader { private File delete_this_tmp_dbf = null; public static final String FILE_PROPERTY_KEY = "File"; public static final String DEFAULT_VALUE_PROPERTY_KEY = "DefaultValue"; public static final String COMPRESSED_FILE_PROPERTY_KEY = "CompressedFile"; /** Creates new ShapeReader */ public ShapefileReader() { } /** * Main method to read a shapefile. * Most of the work is done in the org.geotools.* package. * * @param dp 'InputFile' or 'DefaultValue' to specify output .shp file. * @return a FeatureCollection created from .shp and .dbf (dbf is optional) */ public FeatureCollection read(DriverProperties dp) throws IllegalParametersException, Exception { String shpfileName = dp.getProperty(FILE_PROPERTY_KEY); if (shpfileName == null) { shpfileName = dp.getProperty(DEFAULT_VALUE_PROPERTY_KEY); } if (shpfileName == null) { throw new IllegalParametersException(I18N.get("io.ShapefileReader.no-file-property-specified")); } int loc = shpfileName.lastIndexOf(File.separatorChar); String path = shpfileName.substring(0, loc + 1); // ie. "/data1/hills.shp" -> "/data1/" String fname = shpfileName.substring(loc + 1); // ie. "/data1/hills.shp" -> "hills.shp" loc = fname.lastIndexOf("."); if (loc == -1) { throw new IllegalParametersException(I18N.get("io.ShapefileReader.filename-must-end-in-shp")); } String fnameWithoutExtention = fname.substring(0, loc); // ie. "hills.shp" -> "hills" String dbfFileName = path + fnameWithoutExtention + ".dbf"; //okay, have .shp and .dbf file paths, lets create Shapefile and DbfFile Shapefile myshape = getShapefile(shpfileName, dp.getProperty(COMPRESSED_FILE_PROPERTY_KEY)); String charsetName = dp.getProperty("charset"); if (charsetName == null) charsetName = Charset.defaultCharset().name(); DbfFile mydbf = getDbfFile(dbfFileName, dp.getProperty(COMPRESSED_FILE_PROPERTY_KEY), Charset.forName(charsetName)); GeometryFactory factory = new GeometryFactory(); GeometryCollection collection = null; try { collection = myshape.read(factory); } finally { myshape.close(); //ensure we can delete input shape files before task is closed } FeatureSchema fs = new FeatureSchema(); // fill in schema fs.addAttribute("GEOMETRY", AttributeType.GEOMETRY); FeatureCollection featureCollection = null; if ( mydbf == null ) { // handle shapefiles without dbf files. featureCollection = new FeatureDataset(fs); int numGeometries = collection.getNumGeometries(); for (int x = 0; x < numGeometries; x++) { Feature feature = new BasicFeature(fs); Geometry geo = collection.getGeometryN(x); feature.setGeometry(geo); featureCollection.add(feature); } } else { // There is a DBF file so we have to set the Charset to use and // to associate the attributes in the DBF file with the features. if (mydbf.getLastRec()-1 > collection.getNumGeometries()) { throw new ShapefileException("Error : shp shape number does not match dbf record number"); } int numfields = mydbf.getNumFields(); for (int j = 0; j < numfields; j++) { AttributeType type = AttributeType.toAttributeType(mydbf.getFieldType(j)); fs.addAttribute( mydbf.getFieldName(j), type ); } featureCollection = new FeatureDataset(fs); for (int x = 0; x < mydbf.getLastRec(); x++) { Feature feature = new BasicFeature(fs); Geometry geo = collection.getGeometryN(x); //StringBuffer s = mydbf.GetDbfRec(x); //[sstein 9.Sept.08] byte[] s = mydbf.GetDbfRec(x); //[sstein 9.Sept.08] for (int y = 0; y < numfields; y++) { feature.setAttribute(y + 1, mydbf.ParseRecordColumn(s, y)); } feature.setGeometry(geo); featureCollection.add(feature); } mydbf.close(); deleteTmpDbf(); // delete dbf file if it was decompressed } return featureCollection; } protected Shapefile getShapefile(String shpfileName, String compressedFname) throws Exception { InputStream in = CompressedFile.openFile(shpfileName,compressedFname); Shapefile myshape = new Shapefile(in); return myshape; } /** * Get's a DbfFile. * For compatibilty reasons, this method is a wrapper to the new with * Charset functions. * * @param dbfFileName * @param compressedFname * @return a DbfFile object for the dbf file named FileName * @throws Exception */ protected DbfFile getDbfFile(String dbfFileName, String compressedFname) throws Exception { return getDbfFile(dbfFileName, compressedFname, Charset.defaultCharset()); } protected DbfFile getDbfFile(String dbfFileName, String compressedFname, Charset charset) throws Exception { DbfFile mydbf = null; if ((compressedFname != null) && (compressedFname.length() > 0)) { byte[] b = new byte[16000]; int len; boolean keepGoing = true; // copy the file then use that copy File file = File.createTempFile("dbf", ".dbf"); FileOutputStream out = new FileOutputStream(file); InputStream in = CompressedFile.openFile(dbfFileName,compressedFname); while (keepGoing) { len = in.read(b); if (len > 0) { out.write(b, 0, len); } keepGoing = (len != -1); } in.close(); out.close(); mydbf = new DbfFile(file.toString(), charset); delete_this_tmp_dbf = file; // to be deleted later on } else { File dbfFile = new File( dbfFileName ); if ( dbfFile.exists() ) { mydbf = new DbfFile(dbfFileName, charset); } } return mydbf; } private void deleteTmpDbf() { if (delete_this_tmp_dbf != null) { delete_this_tmp_dbf.delete(); delete_this_tmp_dbf = null; } } }