// docs start source /* * GeoTools - The Open Source Java GIS Tookit * http://geotools.org * * (C) 2006-2008, Open Source Geospatial Foundation (OSGeo) * * This file is hereby placed into the Public Domain. This means anyone is * free to do whatever they wish with this file. Use it well and enjoy! */ package org.geotools.demo; import com.vividsolutions.jts.geom.Geometry; import java.awt.event.ActionEvent; import java.io.File; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.JToolBar; import org.geotools.data.DataStore; import org.geotools.data.DataStoreFactorySpi; import org.geotools.data.DefaultQuery; import org.geotools.data.DefaultTransaction; import org.geotools.data.FeatureSource; import org.geotools.data.FeatureStore; import org.geotools.data.FeatureWriter; import org.geotools.data.FileDataStore; import org.geotools.data.FileDataStoreFinder; import org.geotools.data.Transaction; import org.geotools.data.shapefile.ShapefileDataStoreFactory; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.JTS; import org.geotools.map.DefaultMapContext; import org.geotools.map.MapContext; import org.geotools.referencing.CRS; import org.geotools.swing.JMapFrame; import org.geotools.swing.ProgressWindow; import org.geotools.swing.action.SafeAction; import org.geotools.swing.data.JFileDataStoreChooser; import org.jdesktop.swingworker.SwingWorker; import org.opengis.feature.Feature; import org.opengis.feature.FeatureVisitor; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.FeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; /** * This is a visual example of changing the coordinate reference * system of a feature layer. * * @source $URL$ */ public class CRSLab { private File sourceFile; private FeatureSource<SimpleFeatureType,SimpleFeature> featureSource; private MapContext map; private int numInvalidGeometries = 0; public static void main(String[] args) throws Exception { CRSLab lab = new CRSLab(); lab.displayShapefile(); } // docs end main // docs start display /** * This method: * <ol type="1"> * <li> Prompts the user for a shapefile to display * <li> Creates a JMapFrame with custom toolbar buttons * <li> Displays the shapefile * </ol> */ private void displayShapefile() throws Exception { sourceFile = JFileDataStoreChooser.showOpenFile("shp", null); if (sourceFile == null) { return; } FileDataStore store = FileDataStoreFinder.getDataStore(sourceFile); featureSource = store.getFeatureSource(); // Create a map context and add our shapefile to it map = new DefaultMapContext(); map.addLayer(featureSource, null); /* * Create a JMapFrame with custom toolbar buttons */ JMapFrame mapFrame = new JMapFrame(map); mapFrame.enableTool(JMapFrame.Tool.NONE); mapFrame.enableStatusBar(true); JToolBar toolbar = mapFrame.getToolBar(); toolbar.add( new JButton( new ValidateGeometryAction() ) ); toolbar.add( new JButton( new ExportShapefileAction() ) ); /* * Display the map frame. When it is closed the application * will exit */ mapFrame.setSize(800, 600); mapFrame.setVisible(true); } // docs end display // docs start export /** * Export features to a new shapefile using the map projection in which * they are currently displayed */ private void exportToShapefile() throws Exception { SimpleFeatureType schema = featureSource.getSchema(); JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp"); chooser.setDialogTitle("Save reprojected shapefile"); chooser.setSaveFile(sourceFile); int returnVal = chooser.showSaveDialog(null); if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) { return; } File file = chooser.getSelectedFile(); if (file.equals(sourceFile)) { JOptionPane.showMessageDialog(null, "Cannot replace " + file ); return; } // set up the math transform used to process the data CoordinateReferenceSystem dataCRS = schema.getCoordinateReferenceSystem(); CoordinateReferenceSystem worldCRS = map.getCoordinateReferenceSystem(); boolean lenient = true; // allow for some error due to different datums MathTransform transform = CRS.findMathTransform( dataCRS, worldCRS, lenient ); // grab all features FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = featureSource.getFeatures(); // And create a new Shapefile with a slight modified schema DataStoreFactorySpi factory = new ShapefileDataStoreFactory(); Map<String, Serializable> create = new HashMap<String, Serializable>(); create.put("url", file.toURI().toURL()); create.put("create spatial index", Boolean.TRUE); DataStore newDataStore = factory.createNewDataStore(create); SimpleFeatureType featureType = SimpleFeatureTypeBuilder.retype( schema, worldCRS ); newDataStore.createSchema( featureType ); // carefully open an iterator and writer to process the results Transaction transaction = new DefaultTransaction("Reproject"); FeatureWriter<SimpleFeatureType, SimpleFeature> writer = newDataStore.getFeatureWriterAppend( featureType.getTypeName(), transaction); FeatureIterator<SimpleFeature> iterator = featureCollection.features(); try { while( iterator.hasNext() ){ // copy the contents of each feature and transform the geometry SimpleFeature feature = iterator.next(); SimpleFeature copy = writer.next(); copy.setAttributes( feature.getAttributes() ); Geometry geometry = (Geometry) feature.getDefaultGeometry(); Geometry geometry2 = JTS.transform(geometry, transform); copy.setDefaultGeometry( geometry2 ); writer.write(); } transaction.commit(); JOptionPane.showMessageDialog(null, "Export to shapefile complete" ); } catch (Exception problem) { problem.printStackTrace(); transaction.rollback(); JOptionPane.showMessageDialog(null, "Export to shapefile failed" ); } finally { writer.close(); iterator.close(); transaction.close(); } } // docs end export // docs start export2 /** * Export features to a new shapefile using the map projection in which * they are currently displayed */ private void exportToShapefile2() throws Exception { FeatureType schema = featureSource.getSchema(); String typeName = schema.getName().getLocalPart(); JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp"); chooser.setDialogTitle("Save reprojected shapefile"); chooser.setSaveFile(sourceFile); int returnVal = chooser.showSaveDialog(null); if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) { return; } File file = chooser.getSelectedFile(); if (file.equals(sourceFile)) { JOptionPane.showMessageDialog( null, "Cannot replace " + file, "File warning", JOptionPane.WARNING_MESSAGE); return; } // We can now query to retrieve a FeatureCollection // in the desired coordinate reference system DefaultQuery query = new DefaultQuery(); query.setTypeName(typeName); query.setCoordinateSystemReproject(map.getCoordinateReferenceSystem()); FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = featureSource.getFeatures(query); // And create a new Shapefile with the results DataStoreFactorySpi factory = new ShapefileDataStoreFactory(); Map<String, Serializable> create = new HashMap<String, Serializable>(); create.put("url", file.toURI().toURL()); create.put("create spatial index", Boolean.TRUE); DataStore newDataStore = factory.createNewDataStore(create); newDataStore.createSchema(featureCollection.getSchema()); Transaction transaction = new DefaultTransaction("Reproject"); FeatureStore<SimpleFeatureType, SimpleFeature> featureStore; featureStore = (FeatureStore<SimpleFeatureType, SimpleFeature>) newDataStore.getFeatureSource(typeName); featureStore.setTransaction(transaction); try { featureStore.addFeatures(featureCollection); transaction.commit(); JOptionPane.showMessageDialog(null, "Export to shapefile complete", "Export", JOptionPane.INFORMATION_MESSAGE); } catch (Exception problem) { problem.printStackTrace(); transaction.rollback(); JOptionPane.showMessageDialog(null, "Export to shapefile failed", "Export", JOptionPane.ERROR_MESSAGE); } finally { transaction.close(); } } // docs end export2 // docs start validate /** * Check the Geometry (point, line or polygon) of each feature to make * sure that it is topologically valid and report on any errors found. * <p> * See also the nested ValidateGeometryAction class below which runs * this method in a background thread and reports on the results */ private void validateFeatureGeometry() throws Exception { final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = featureSource.getFeatures(); /* * Rather than use an iterator, we create a FeatureVisitor to * check the Geometry of each feature */ FeatureVisitor visitor = new FeatureVisitor() { public void visit(Feature feature) { Geometry geom = (Geometry) feature.getDefaultGeometryProperty().getValue(); if (geom != null && !geom.isValid()) { numInvalidGeometries++; System.out.println("Invalid Geoemtry: " + feature.getIdentifier()); } } }; /* * For shapefiles with many features its nice to display * a progress bar */ final ProgressWindow progress = new ProgressWindow(null); progress.setTitle("Validating feature geometry"); /* * Now we pass the visitor and the progress bar to the feature * collection */ numInvalidGeometries = 0; featureCollection.accepts(visitor, progress); } // docs end validate // docs start export action /** * This class performs the task of exporting the features to a new shapefile * using the map projection that they are currently displayed in. It also * supplies the name and tool tip for the toolbar button. */ class ExportShapefileAction extends SafeAction { ExportShapefileAction() { super("Export..."); putValue(Action.SHORT_DESCRIPTION, "Export features in the current projection"); } @Override public void action(ActionEvent e) throws Throwable { exportToShapefile(); } } // docs end export action // docs start validate action /** * This class performs the task of checking that the Geometry of each * feature is topologically valid and reports on the results. It also * supplies the name and tool tip for the toolbar button. */ class ValidateGeometryAction extends SafeAction { ValidateGeometryAction() { super("Validate geometry"); putValue(Action.SHORT_DESCRIPTION, "Check the geometry of each feature"); } @Override public void action(ActionEvent e) throws Throwable { /* * Here we use the SwingWorker helper class to run the * validation routine in a background thread, otherwise * the GUI would wait and the progress bar would not be * displayed properly */ SwingWorker worker = new SwingWorker<String, Object>() { @Override protected String doInBackground() throws Exception { validateFeatureGeometry(); if (numInvalidGeometries == 0) { return "All feature geometries are valid"; } else { StringBuilder sb = new StringBuilder(); sb.append(numInvalidGeometries); sb.append(" feature"); if (numInvalidGeometries > 1) { sb.append("s"); } sb.append(" with invalid geometries (see console output)"); return sb.toString(); } } @Override protected void done() { try { JOptionPane.showMessageDialog(null, get(), "Geometry results", JOptionPane.INFORMATION_MESSAGE); } catch (Exception ignore) { } } }; /* * This statement runs the validateion method in a background thread */ worker.execute(); } } // docs end validate action }