package jeql.io.shapefile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import jeql.api.error.ExecutionException;
import jeql.api.row.BasicRow;
import jeql.api.row.Row;
import jeql.api.row.RowIterator;
import jeql.api.row.RowSchema;
import jeql.util.TypeUtil;
import org.geotools.dbffile.DbfFile;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
/**
* ShapefileRowInputStream reads Shapefiles.
* DBF files are optional - if one is not present
* only the geometry is read.
*/
public class ShapefileRowIterator
implements RowIterator
{
private Shapefile shapeStream;
private DbfFile dbf;
private int count = 0;
private int numDbfFields;
private GeometryFactory geomFactory = new GeometryFactory();
private RowSchema schema;
public ShapefileRowIterator() {
}
public RowSchema getSchema() { return schema; }
public void open(String shpFilename)
throws Exception
{
open(shpFilename, true);
}
public void open(String shpFilename, boolean readDBF)
throws Exception
{
// System.out.println("separator:" + File.separatorChar);
int sepLoc = shpFilename.lastIndexOf(File.separatorChar);
String path = shpFilename.substring(0, sepLoc + 1); // ie. "/data1/hills.shp" -> "/data1/"
String filename = shpFilename.substring(sepLoc + 1); // ie. "/data1/hills.shp" -> "hills.shp"
int dotLoc = filename.lastIndexOf(".");
if (dotLoc == -1) {
throw new IllegalArgumentException("Filename must end in '.shp'");
}
String filenameWithoutExtension = filename.substring(0, dotLoc); // ie. "hills.shp" -> "hills."
String dbfFileName = path + filenameWithoutExtension + ".dbf";
if (! readDBF) {
dbfFileName = null;
}
open(shpFilename, dbfFileName);
}
/**
* Opens a shapefile and optional DBF file.
*
* @param shpFilename
* @param dbfFilename name of dbf file, or null if not to be read
* @throws Exception
*/
private void open(String shpFilename, String dbfFilename)
throws Exception
{
shapeStream = getShapefile(shpFilename);
shapeStream.readStream(geomFactory);
// try to open DBF file if given, but continue even if it is not present
dbf = null;
if (dbfFilename != null) {
try {
dbf = getDbfFile(dbfFilename);
} catch (FileNotFoundException ex) {
// null indicates no dbf present
dbf = null;
}
}
createSchema();
}
private void createSchema()
{
numDbfFields = 0;
if (dbf != null)
numDbfFields = dbf.getNumFields();
schema = new RowSchema(numDbfFields + 1);
// fill in schema
schema.setColumnDef(0, "GEOMETRY", Geometry.class);
// dbf fields are not added if no dbf is present
if (dbf == null)
return;
for (int j = 0; j < numDbfFields; j++) {
String typename = dbf.getFieldType(j);
schema.setColumnDef(j + 1, dbf.getFieldName(j), TypeUtil.typeForName(typename));
}
}
public Row next()
{
try {
return nextRaw();
}
catch (IOException ex) {
throw new ExecutionException(ex);
}
}
public Row nextRaw() throws IOException
{
Geometry geom = shapeStream.next();
if (geom == null)
return null;
BasicRow row = new BasicRow(schema.size());
row.setValue(0, geom);
if (dbf == null)
return row;
StringBuffer s = dbf.getNextDbfRec();
count++;
for (int y = 0; y < numDbfFields; y++) {
try {
row.setValue(y + 1, dbf.ParseRecordColumn(s, y));
} catch (Exception ex) {
/**
* don't propagate exceptions from datatype problems
* what *should* happen is that ParseRecordColumn does not throw any exceptions
* but simply returns a null value of the right type
*
*/
}
}
return row;
}
public RowSchema getFeatureSchema() {
return schema;
}
public void close() throws IOException
{
shapeStream.close();
if (dbf != null)
dbf.close();
}
protected Shapefile getShapefile(String shpfileName)
throws Exception {
java.io.InputStream in = new FileInputStream(shpfileName);
Shapefile shpfile = new Shapefile(in);
return shpfile;
}
protected DbfFile getDbfFile(String dbfFileName)
throws Exception {
DbfFile dbf = new DbfFile(dbfFileName);
return dbf;
}
}