package uk.ac.leeds.ccg.geotools; import java.lang.*; import cmp.LEDataStream.*; import uk.ac.leeds.ccg.shapefile.*; import uk.ac.leeds.ccg.dbffile.*; import java.io.*; import java.net.*; import java.util.zip.*; import java.util.*; import java.awt.Color; /** * A class to simplify the process of loading an ESRI(r) Shapefile. * It is effectivly a wrapper for the Shapefile class.
* This class will open the shapefile (both .shp and .dbf if avaiable) * after which calls to readLines,readPolygons and readData will extract * the necasary information out of the shapefile.
* The class also has a series of getTheme() methods that encapsulate the process
* of building themes (complet with layer and shaders) as a v.easy way of setting up a theme
* for inclusion in a viewer.
* @author James Macgill, Center for computational Geography.
* @author David Robison ArcM code and bug fixes
*/
public class ShapefileReader extends java.lang.Object implements DataSource {
public static final String cvsid="$Id: ShapefileReader.java,v 1.35 2002/05/21 14:30:31 ianturton Exp $";
private final static boolean DEBUG=true;
public Shapefile sf = null;
public Dbf dbf = null;
private int idCol = -1;//best general guess
private String name = "none";
private MixedLayer map = new MixedLayer(this);
private Loader loader;
/**
* Creates and opens a new ShapefileReader
* @param baseFilename: name of file without extention e.g. 'name' not 'name.shp'
*/
public ShapefileReader(String baseFilename){
if(DEBUG)System.out.println("---->uk.ac.leeds.ccg.geotools.ShapefileReader constructed. Will identify itself as SfR->");
name = baseFilename;
try{
sf = new Shapefile(baseFilename+".shp");
} catch(Exception e)
{System.out.println("SfR->"+e);return;}
try{
File dbffile = new File(baseFilename+".dbf");
dbf = new Dbf(dbffile);
getIds();
} catch(Exception e) {System.out.println("SfR->"+e);return;}
}
public ShapefileReader(String baseFilename, int idCol){
if(DEBUG)System.out.println("---->uk.ac.leeds.ccg.geotools.ShapefileReader constructed. Will identify itself as SfR->");
name = baseFilename;
try {
sf = new Shapefile(baseFilename+".shp");
} catch(Exception e)
{System.out.println("SfR->"+e);}
try {
File dbffile = new File(baseFilename+".dbf");
dbf = new Dbf(dbffile);
setIdCol(idCol);
getIds();
} catch(Exception e)
{System.out.println("SfR->"+e);}
}
URL base;
int status = 0;
/**
* Creates and opens a new ShapefileReader
* If at all posible, this is the contstuctor that should be used.
* @param base: url of file without extention e.g. 'name' not 'name.shp'
* @param idCol An int giving the number of the colum to take the feature ids from.
*/
public ShapefileReader(URL base,int idCol){
if(DEBUG)System.out.println("---->uk.ac.leeds.ccg.geotools.ShapefileReader constructed. Will identify itself as SfR->");
//if(base.toString().endsWith(".shp")
setIdCol(idCol);
this.base = base;
map.setStatus(map.PENDING);
name = base.getFile();
loader = new Loader();
//loader.start();
//the following block of code effectivly dissables the threaded loading system for now
/*try{
loader.join();
}
catch(InterruptedException ie){
System.err.println("Shapefile not loaded by loader "+ie);
}*/
loader.run();//bypass threading and run from here
System.out.println("Shapefile Constructor Finished - tread loading disabled");
}
/**
* Creates and opens a new ShapefileReader
* @param shp: InputStream to the .shp file
* @param dbf: InputStream to the .dbf file (can be null)
* @param idCol An int giving the number of the colum to take the feature ids from.
*/
public ShapefileReader(InputStream shpIn,InputStream dbfIn,int idCol){
if(DEBUG)System.out.println("---->uk.ac.leeds.ccg.geotools.ShapefileReader constructed. Will identify itself as SfR->");
//name = ???
try{
sf = new Shapefile(shpIn);
if(dbfIn!=null){
dbf = new Dbf(dbfIn);
}
setIdCol(idCol);
getIds();
}catch(Exception e){System.err.println("SfR->"+e);}
}
/**
* Creates and opens a new ShapefileReader
* @param shp: InputStream to the .shp file
* @param dbf: InputStream to the .dbf file (can be null)
* @param idCol An int giving the number of the colum to take the feature ids from.
*/
public ShapefileReader(InputStream shpIn,InputStream dbfIn){
if(DEBUG)System.out.println("---->uk.ac.leeds.ccg.geotools.ShapefileReader constructed with inputstreams. Will identify itself as SfR->");
//name = ???
try{
sf = new Shapefile(shpIn);
if(dbfIn!=null){
dbf = new Dbf(dbfIn);
}
}catch(Exception e){System.err.println("SfR->"+e);}
guessIdCol();
getIds();
}
/**
* Constructs a shapefile reader from a base url.
* This is the best constructor to use if you do not know the id column for the shapefile
* @param base The base url for the shapefile (i.e. leave the .shp off the end if possible)
*/
public ShapefileReader(URL base){
this(base,-1);
//if(DEBUG)System.out.println("---->uk.ac.leeds.ccg.geotools.ShapefileReader constructed. Will identify itself as SfR->");
//try and figure out id col
}
private void guessIdCol(){
switch(sf.getShapeType()){
case(Shapefile.POLYGON):
idCol = 2;
break;
case(Shapefile.ARC_M):
idCol = 5;
break;
case(Shapefile.ARC):
idCol = 5;
break;
case(Shapefile.POINT):
idCol = 3;
break;
}
if(DEBUG)System.out.println("SfR->ID col first guessed at "+idCol+" "+dbf.getNumFields());
// attempt to solve sourceforge bug #122495 - ian
if(dbf==null) return;
if(idCol> dbf.getNumFields()-1){// whoops guessed wrong!
idCol= -1; // probably not right but safe
}
if(DEBUG)System.out.println("SfR->ID col guessed at "+idCol+" from "+dbf.getNumFields());
}
public void setIdCol(int col){
idCol = col;
if(DEBUG&&col<0) System.out.println("going for sequential ids");
//getIds();
}
public void setIdCol(String name){
int col = dbf.getFieldNumber(name);
idCol = (col>=0)?col:idCol;
getIds();
}
public int getIdCol(){
return idCol;
}
/**
* gets the shape type for this shapefile.
*
* @return int The type of feature, constants defined in shapefile
* match the values given, for example Shapefile.POLYGON;
*
*/
public int getShapeType(){
return sf.getShapeType();
}
/**
* Reads the data in this shapefile and produces a theme for
* use in a Viewer.
* @return Theme a theme containing the features of the shapefile
*/
public Theme getTheme(){
Theme t = new Theme(this.getLayer());
t.setName(name);
return t;
}
/**
* Reads the data in this shapefile and produces a layer for
* use in a Theme.
* @return Layer a Layer containing the features of the shapefile
*/
public Layer getLayer(){
return map;
//return l;
}
/**
* Gets a theme based on the given shader and colName
* as a bonus the method configures the shaders range
* for you by reading the min and max values from the named column
* @param shade a RampShader to shade this theme (includes HSV,sat and val shaders)
* @param colName the name of the column to grab the data from
*/
public Theme getTheme(Shader shade,String colName){
Theme t = getTheme();
SimpleGeoData data = new SimpleGeoData();
data.setName(colName);
t.setGeoData(data);
shade.setRange(data);
t.setShader(shade);
AddDataWhenReady dataWatch = new AddDataWhenReady(data,colName);
dataWatch.start();
return t;
}
class AddDataWhenReady extends Thread{
SimpleGeoData data;
String colName;
int col;
public AddDataWhenReady(SimpleGeoData data,String colName){
this.data = data;
this.colName = colName;
}
public AddDataWhenReady(SimpleGeoData data,int col){
this.data = data;
this.colName = colName;
}
public void run(){
System.out.println("Waiting for data loader to finish");
try{
loader.join();
}
catch(InterruptedException ie){
System.err.println("AddDataWhenReady stuck because "+ie);
return;
}
System.out.println("Apparently data loader has now finished");
if(colName !=null){
col = dbf.getFieldNumber(colName);
}
if(col>=0){
readDataNow(data,col);
}
else{
if(DEBUG)System.err.println("SfR->Column "+colName+" not found in .dbf file");
}
//map.notifyLayerChangedListeners(LayerChangedEvent.DATA);
}
}
/**
* convinience version of get theme (shade,colname)
* provides a default satShader with the specified color
* @param colName A string containing the name of the .dbf col to shade by
* @param c A color to shade with.
*/
public Theme getTheme(String colName,Color c){
return getTheme(new satShader(c),colName);
}
/**
* Reads the feature information from the shapefile
* and produces a LineLayer for use in a Theme.
* This will only work if the feature type is
* line or polygon otherwise it will fail
* @see getShapeType
* @return LineLayer a LineLayer containing the lines from this shapefile.
*/
public Layer readLines(){
// LineLayer map = new LineLayer();
GeoLine gp;
ShapefileShape polys[] = sf.getShapes();
ShapeArc poly;
ShapePoint p[] = null;
double px[],py[];
int nPoints,nParts;
int count = sf.getRecordCount();
if(ids==null) getIds();
for(int i = 0;i