|
From: <jum...@li...> - 2015-04-11 11:59:59
|
Revision: 4395
http://sourceforge.net/p/jump-pilot/code/4395
Author: michaudm
Date: 2015-04-11 11:59:56 +0000 (Sat, 11 Apr 2015)
Log Message:
-----------
fix #396 on ShapefileReader so that it can read shapefile from shx as other GIS software do
Modified Paths:
--------------
core/trunk/src/com/vividsolutions/jump/io/ShapefileReader.java
core/trunk/src/org/geotools/shapefile/Shapefile.java
Modified: core/trunk/src/com/vividsolutions/jump/io/ShapefileReader.java
===================================================================
--- core/trunk/src/com/vividsolutions/jump/io/ShapefileReader.java 2015-04-10 12:24:17 UTC (rev 4394)
+++ core/trunk/src/com/vividsolutions/jump/io/ShapefileReader.java 2015-04-11 11:59:56 UTC (rev 4395)
@@ -93,6 +93,7 @@
public class ShapefileReader extends AbstractJUMPReader {
private File delete_this_tmp_dbf = null;
+ private File delete_this_tmp_shx = null;
private static Logger LOG = Logger.getLogger(ShapefileReader.class);
@@ -114,7 +115,7 @@
*/
public FeatureCollection read(DriverProperties dp)
throws IllegalParametersException, Exception {
-
+ long t0 = System.currentTimeMillis();
getExceptions().clear();
// ATTENTION: this can contain a zip file path as well
@@ -136,11 +137,13 @@
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(shpFileName, dp.getProperty(COMPRESSED_FILE_PROPERTY_KEY), Charset.forName(charsetName));
+ DbfFile mydbf = getDbfFile(shpFileName, dp.getProperty(COMPRESSED_FILE_PROPERTY_KEY),
+ Charset.forName(charsetName));
+ InputStream shx = getShx(shpFileName, dp.getProperty(COMPRESSED_FILE_PROPERTY_KEY));
GeometryFactory factory = new GeometryFactory();
GeometryCollection collection = null;
try {
- collection = myshape.read(factory);
+ collection = shx == null ? myshape.read(factory) : myshape.readFromIndex(factory, shx);
} finally {
myshape.close(); //ensure we can delete input shape files before task is closed
}
@@ -229,8 +232,9 @@
mydbf.close();
deleteTmpDbf(); // delete dbf file if it was decompressed
+ deleteTmpShx(); // delete shx file if it was decompressed
}
-
+ System.out.println("Shapfile read in " + (System.currentTimeMillis()-t0) + " ms");
return featureCollection;
}
@@ -242,6 +246,59 @@
return myshape;
}
+ protected InputStream getShx(String srcFileName, String compressedFname) throws Exception {
+ FileInputStream shxInputStream;
+
+ // default is a *.shx src file
+ if (srcFileName.matches("(?i).*\\.shp$")) {
+ // replace file name extension of compressedFname (probably .shp) with .shx
+ srcFileName = srcFileName.replaceAll("\\.[^.]*$", ".shx");
+ File shxFile = new File( srcFileName );
+ if ( shxFile.exists() )
+ return new FileInputStream(srcFileName);
+ }
+ // if we are in an archive that can hold multiple files compressedFname is defined and a String
+ else if (compressedFname instanceof String) {
+ byte[] b = new byte[16000];
+ int len;
+ boolean keepGoing = true;
+
+ // copy the file then use that copy
+ File file = File.createTempFile("shx", ".shx");
+ FileOutputStream out = new FileOutputStream(file);
+
+ // replace file name extension of compressedFname (probably .shp) with .dbf
+ compressedFname = compressedFname.replaceAll("\\.[^.]*$", ".shx");
+
+ try {
+ InputStream in = CompressedFile.openFile(srcFileName,compressedFname);
+
+ while (keepGoing) {
+ len = in.read(b);
+
+ if (len > 0) {
+ out.write(b, 0, len);
+ }
+
+ keepGoing = (len != -1);
+ }
+
+ in.close();
+ out.close();
+
+ shxInputStream = new FileInputStream(file.toString());
+ delete_this_tmp_shx = file; // to be deleted later on
+ return shxInputStream;
+ } catch (Exception e) {
+ String msg = e.getMessage();
+ LOG.info(msg);
+ System.err.println(msg);
+ }
+ }
+
+ return null;
+ }
+
/**
* Get's a DbfFile.
* For compatibilty reasons, this method is a wrapper to the new with
@@ -318,4 +375,11 @@
delete_this_tmp_dbf = null;
}
}
+
+ private void deleteTmpShx() {
+ if (delete_this_tmp_shx != null) {
+ delete_this_tmp_shx.delete();
+ delete_this_tmp_shx = null;
+ }
+ }
}
Modified: core/trunk/src/org/geotools/shapefile/Shapefile.java
===================================================================
--- core/trunk/src/org/geotools/shapefile/Shapefile.java 2015-04-10 12:24:17 UTC (rev 4394)
+++ core/trunk/src/org/geotools/shapefile/Shapefile.java 2015-04-11 11:59:56 UTC (rev 4395)
@@ -3,7 +3,9 @@
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.List;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jump.io.EndianDataInputStream;
@@ -339,5 +341,110 @@
int pos=0, len=0;
file.close();
}
+
+ /**
+ * The purpose of this new reader [mmichaud 2015-04-11] is to read a shapefile using the shx
+ * index file.
+ * While the legacy reader #read(GeometryFactory geometryFactory) read the shapefile sequentially
+ * and don't need the shx index file, this new parser read the shx file and access the shp file
+ * with a RandomAccessReader.
+ * Because the shapefile may come from a compressed input stream, the method first write the
+ * shapefile in a temporary file.
+ * @param geometryFactory
+ * @param is
+ * @return a GeometryCollection containing all the shapes.
+ * @throws IOException
+ * @throws ShapefileException
+ * @throws Exception
+ */
+ public synchronized GeometryCollection readFromIndex(GeometryFactory geometryFactory, InputStream is)
+ throws Exception {
+
+ // Flush shapefile inputStream to a temporary file, because inputStream
+ // may come from a zipped archive, and we want to access data in Random mode
+ File tmpShp = null;
+ RandomAccessFile raf = null;
+ BufferedInputStream bis = null;
+ BufferedOutputStream bos = null;
+ try {
+ tmpShp = File.createTempFile("tmpshp", ".shp");
+ bis = new BufferedInputStream(myInputStream, 4096);
+ bos = new BufferedOutputStream(new FileOutputStream(tmpShp), 4096);
+ int nb = 0;
+ byte[] bytes = new byte[4096];
+ while (-1 != (nb = bis.read(bytes))) {
+ bos.write(bytes, 0, nb);
+ }
+ raf = new RandomAccessFile(tmpShp, "r");
+ } catch(IOException e) {
+ throw e;
+ } finally {
+ if (bis != null) bis.close();
+ if (bos != null) bos.close();
+ }
+
+ // read shapefile header
+ byte[] bytes = new byte[100];
+ ByteBuffer bb = ByteBuffer.wrap(bytes);
+ raf.getChannel().read(bb);
+ EndianDataInputStream shp = new EndianDataInputStream(new ByteArrayInputStream(bytes));
+ if(shp==null) throw new IOException("Failed connection or no content for " + tmpShp);
+ ShapefileHeader shpMainHeader = new ShapefileHeader(shp);
+ if(shpMainHeader.getVersion() < VERSION){System.err.println("Sf-->Warning, Shapefile format ("+shpMainHeader.getVersion()+") older that supported ("+VERSION+"), attempting to read anyway");}
+ if(shpMainHeader.getVersion() > VERSION){System.err.println("Sf-->Warning, Shapefile format ("+shpMainHeader.getVersion()+") newer that supported ("+VERSION+"), attempting to read anyway");}
+
+ // read shx header
+ EndianDataInputStream shx = new EndianDataInputStream(is);
+ if(shx==null) throw new IOException("Failed connection to shx");
+ ShapefileHeader shxMainHeader = new ShapefileHeader(shx);
+ if(shxMainHeader.getVersion() < VERSION){System.err.println("Sf-->Warning, Shapefile format ("+shxMainHeader.getVersion()+") older that supported ("+VERSION+"), attempting to read anyway");}
+ if(shxMainHeader.getVersion() > VERSION){System.err.println("Sf-->Warning, Shapefile format ("+shxMainHeader.getVersion()+") newer that supported ("+VERSION+"), attempting to read anyway");}
+
+ Geometry body;
+ ArrayList list = new ArrayList();
+ int type = shpMainHeader.getShapeType();
+ ShapeHandler handler = getShapeHandler(type);
+ if(handler==null) throw new ShapeTypeNotSupportedException("Unsupported shape type:" + type);
+
+ int recordNumber = 0;
+ try {
+ while (true) {
+ int offset = shx.readIntBE();
+ int length = shx.readIntBE();
+ recordNumber++;
+ try{
+ bytes = new byte[length*2];
+ bb = ByteBuffer.wrap(bytes);
+ raf.getChannel().read(bb, offset*2 + 8);
+ shp = new EndianDataInputStream(new ByteArrayInputStream(bytes));
+ body = handler.read(shp, geometryFactory, length);
+ list.add(body);
+ if (body.getUserData() != null) errors++;
+ // System.out.println("Done record: " + recordNumber);
+ } catch(IllegalArgumentException r2d2) {
+ System.err.println("Error processing record " + recordNumber + " : " + r2d2.getMessage());
+ System.err.println(" an empty Geometry has been returned");
+ r2d2.printStackTrace();
+ list.add(handler.getEmptyGeometry(geometryFactory));
+ errors++;
+ } catch(Exception c3p0) {
+ System.err.println("Error processing record " + recordNumber + " : " + c3p0.getMessage());
+ System.err.println(" an empty Geometry has been returned");
+ c3p0.printStackTrace();
+ list.add(handler.getEmptyGeometry(geometryFactory));
+ errors++;
+ }
+ }
+ } catch(EOFException e) {}
+ if (raf != null) {
+ raf.close();
+ }
+ if (tmpShp != null && tmpShp.exists()) {
+ tmpShp.delete();
+ }
+
+ return geometryFactory.createGeometryCollection((Geometry[])list.toArray(new Geometry[]{}));
+
+ }
}
|