package test;
/**
 * @author Jeroen van Dijk
 * @date Mar 12, 2007
 * @package test
 * @filename GISFeatures.java
 *  
 */


import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.IllegalAttributeException;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;

/**
 * The Class GISFeatures.
 * 
 * @author jeroen
 */
public class GISFeatures {
	/** The log. */
	private static Log log = LogFactory.getLog(GISFeatures.class.getName());
	/**
	 * The main method.
	 * 
	 * @param args the args
	 * @throws IllegalAttributeException 
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException, IllegalAttributeException {
		Envelope envelope = new Envelope(new Coordinate(4.777f, 51.594f));
		GeometryFactory geomFactory = new GeometryFactory();
		Geometry tileGeometry = geomFactory.toGeometry(envelope);
		double distance = 0.01;
		
		
		GISLoader gisLoader = new GISLoader("shapefiles.xml");
		gisLoader.read();
		
			URL shapeURL = gisLoader.getFile(0).toURI().toURL();
			GISFeatures gisFeatures = new GISFeatures(shapeURL);
			gisFeatures.process();
//			System.out.print(gisFeatures.getAllFeatures(shapeURL));
			gisFeatures.write(tileGeometry, distance, new File("test_class.shp"));
//			gisFeatures.write(new File("output.shp"));
			log.info("shapefiles written");	
	}
	
	
	/** The datastore of the shapefile. */
	private ShapefileDataStore ds;
	
	/** The feature source. */
	private FeatureSource fs;
	
	/** The feature collection. */
	private FeatureCollection fc;
	
	/** The shapefile URL. */
	private URL shapeURL;
		
	/**
	 * The Constructor.
	 * 
	 * @param shapeURL the shapefile URL
	 */
	public GISFeatures(URL shapeURL) {
		this.shapeURL = shapeURL;		
	}
	
	/**
	 * Processes the loaded shapefile.
	 */
	public void process() {
		//Load shapefile
	    try {
			ds = new ShapefileDataStore(shapeURL);			
			fs = ds.getFeatureSource();
			fc = fs.getFeatures();
			
	    } catch (MalformedURLException e) {
			log.error("Wrong URL format of shapefile URL", e);
		} catch (IOException e) {			
			log.error("I/O error while processing shapefile", e);
		}
	}
	
	/**
	 * Writes the features within a given distance of a geometry to a shapefile.
	 * 
	 * @param distance the distance
	 * @param comparison the comparison
	 * @param outputSHP the output SHP
	 * 
	 * @throws IOException the IO exception
	 * @throws IllegalAttributeException the illegal attribute exception
	 */
	public void write(Geometry comparison, double distance, File outputSHP) throws IOException, IllegalAttributeException {
		Collection<Feature> features = getFeatures(comparison, distance);
		write(features, outputSHP);
	}
	
	/**
	 * Write the feature of the loaded shapefile.
	 * 
	 * @param outputSHP the output SHP
	 * @throws IllegalAttributeException 
	 * @throws IOException 
	 */
	public void write(File outputSHP) throws IOException, IllegalAttributeException {
		Collection<Feature> features = getFeatures();
		write(features, outputSHP);		
	}
	
	/**
	 * Writes a feature collection to a shapefile.
	 * 
	 * @param features the features
	 * @param outputSHP the output SHP
	 * 
	 * @throws IOException the IO exception
	 * @throws IllegalAttributeException the illegal attribute exception
	 */
	public void write(Collection<Feature> features, File outputSHP) throws IOException, IllegalAttributeException {
		  //Create the output shapefile		  		
		ShapefileDataStore outStore = new ShapefileDataStore(outputSHP.toURI().toURL());
	      outStore.createSchema(fs.getSchema());
	      
	      FeatureWriter outFeatureWriter = outStore.getFeatureWriter(outStore.getTypeNames()[0], Transaction.AUTO_COMMIT);	      	     
	      
	      Object[] att = null;	      	     
	      	      
	      for(Feature feature : features) {	      
		      //Get the next, empty feature from the writer              
	          Feature writeFeature = outFeatureWriter.next();
	          
	          att = feature.getAttributes(att);
	          
	          // Set the attributes of the new feature by copying the old, adjusted feature,
	          // which includes the geometry (which is an attribute)	          	         
	          for (int n=0; n<att.length; n++) {
	             writeFeature.setAttribute(n, att[n]);	          
	          }		          	          
	          outFeatureWriter.write();	          	         
	      }	      	     
	      //line below is essential, incomplete files are written without it! 
	      outFeatureWriter.close();

	      if(log.isDebugEnabled())
	    	  log.debug("Number of features written : " + features.size());
	      
	}

	/**
	 * Gets the all features of a shapefile.
	 * 
	 * @param shapeURL the shapefile URL
	 * 
	 * @return the features
	 * 
	 * @throws IOException the IO exception
	 * @throws IllegalAttributeException the illegal attribute exception
	 */
	public static Collection<Feature> getAllFeatures(URL shapeURL)
			throws IllegalAttributeException, IOException {
	   	   	    
	    //Process shapefile    
		ShapefileDataStore ds = new ShapefileDataStore(shapeURL);
	    FeatureSource fs = ds.getFeatureSource();
	    FeatureCollection fc = fs.getFeatures();
	    
	    return getFeatures(fc);	      
	}
	
	/**
	 * Gets the features of the loaded shapefile.
	 * 
	 * @return the features
	 */
	public Collection<Feature> getFeatures() {
		return getFeatures(this.fc);
	}
	
	/**
	 * Gets the features of a given feature collection.
	 * 
	 * @param fc the fc
	 * 
	 * @return the features
	 */
	private static Collection<Feature> getFeatures(FeatureCollection fc) {
		Iterator iterator = fc.iterator();
	    
	    ArrayList<Feature> features = new ArrayList<Feature>();
		 
	    while (iterator.hasNext()) {
			features.add((Feature) iterator.next());		                     	               		   		
	    }
	         
	    return features;
	}
	
	/**
	 * Gets the features of the loaded shapefile within the given distance of the given tile Geometry .
	 * 
	 * @param distance the distance
	 * @param tileGeometry the tile geometry
	 * 
	 * @return the features
	 * 
	 * @throws IllegalAttributeException the illegal attribute exception
	 */
	public Collection<Feature> getFeatures(Geometry tileGeometry, double distance) throws IllegalAttributeException {
			      	    	   
	    ArrayList<Feature> features = new ArrayList<Feature>();
	    //Process shapefile     
	    Iterator iterator = fc.iterator();
	    
	    
         while (iterator.hasNext()) {
            // Extract feature and geometry
            Feature feature = (Feature) iterator.next();
            Geometry featureGeometry = feature.getDefaultGeometry();	           
            
            
            //add feature if it is in the requested area
            if (featureGeometry.isWithinDistance(tileGeometry, distance)) {                                	              	           	            	
            	features.add(feature);	            	
            }
         }
         
         return features;	      
	}	
}
