package net.refractions.linecleaner.ui;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import net.refractions.linecleaner.FeatureUtil;
import net.refractions.linecleaner.cleansing.MinimumLengthProcessor;
import net.refractions.linecleaner.cleansing.PauseMonitor;
import net.refractions.linecleaner.cleansing.PerformCleansingAction;
import net.refractions.linecleaner.cleansing.SimilarLinesProcessor;
import net.refractions.linecleaner.ui.LineCleaningOp.PauseableWizardDialog;
import net.refractions.udig.catalog.CatalogPlugin;
import net.refractions.udig.catalog.IGeoResource;
import net.refractions.udig.catalog.IService;
import net.refractions.udig.project.ILayer;
import net.refractions.udig.project.internal.Layer;
import net.refractions.udig.project.internal.LayerFactory;
import net.refractions.udig.project.internal.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardPage;
import org.geotools.data.FeatureStore;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.indexed.IndexedShapefileDataStoreFactory;
import org.geotools.feature.FeatureCollection;
public class LineCleaningWizard extends Wizard {
private static final String WINDOW_TITLE = "Line Cleaning Wizard";
private List<ILayer> layers;
private DataSelectionPage dataSelectionPage;
private OutputSelectionPage outputSelectionPage;
private OptionsPage optionsPage;
private IGeoResource resource;
private List<FeatureStore> featureStores;
private Layer layer;
public LineCleaningWizard (List<ILayer> layers) {
this.layers = layers;
setWindowTitle(WINDOW_TITLE);
setNeedsProgressMonitor(true);
}
@Override
public void addPages() {
dataSelectionPage = new DataSelectionPage(layers);
outputSelectionPage = new OutputSelectionPage();
optionsPage = new OptionsPage(UiPlugin.getDefault().getDialogSettings());
dataSelectionPage.setWizard(this);
outputSelectionPage.setWizard(this);
optionsPage.setWizard(this);
addPage(dataSelectionPage);
addPage(outputSelectionPage);
addPage(optionsPage);
}
@Override
public boolean performFinish() {
optionsPage.saveSettings();
long start=System.currentTimeMillis();
this.featureStores = getFeatureStores();
final double minimumLength = optionsPage.getMinimumLength();
final double minimumCycleLength = optionsPage.getCyclesLength();
final double nodeDistanceTolerance = optionsPage.getDistanceTolerance();
final double areaTolerance = optionsPage.getAreaTolerance();
final double douglasPeuckerTolerance = optionsPage.getDPTolerance();
final double samplingDistance = optionsPage.getSamplingDistance();
final double verySimilarTolerance = optionsPage.getVerySimilarTolerance();
final double similarTolerance = optionsPage.getSimilarTolerance();
final String outputFileNoExt = outputSelectionPage.getFileNoExtension();
final boolean cleanse = dataSelectionPage.cleanse.getSelection();
final boolean clean = dataSelectionPage.clean.getSelection();
final PauseMonitor pauseMonitor = ((PauseableWizardDialog) getContainer()).pauseMonitor;
IRunnableWithProgress process = new IRunnableWithProgress() {
final int CLEANSE_TICKS = 82;
final int MIN_LENGTH_TICKS = 1;
final int CLEAN_TICKS = 14;
final int MIN_LENGTH_TICKS2 = 1;
final int FINAL_COMMIT_TICKS = 2;
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
try {
if (monitor == null) monitor = new NullProgressMonitor();
// monitor = new MemoryProgressMonitor(monitor, 100000, new Runnable() {
//
// public void run() {
// try {
// System.out.println("Committing...");
// layer.getMapInternal().getEditManagerInternal().commitTransaction();
// Runtime.getRuntime().gc();
//// Thread.sleep(2000);
// } catch (IOException e) {
// new RuntimeException("Unable to commit", e);
//// } catch (InterruptedException e) {
//// e.printStackTrace();
//// System.err.println("Trying to continue...");
// }
// }
//
// });
final int TOTAL_TICKS;
if (cleanse && clean) {
TOTAL_TICKS = CLEANSE_TICKS + MIN_LENGTH_TICKS + CLEAN_TICKS
+ MIN_LENGTH_TICKS2 + FINAL_COMMIT_TICKS;
} else if (cleanse) {
TOTAL_TICKS = CLEANSE_TICKS + MIN_LENGTH_TICKS2 + FINAL_COMMIT_TICKS;
} else {
TOTAL_TICKS = CLEAN_TICKS + MIN_LENGTH_TICKS + FINAL_COMMIT_TICKS;
}
monitor.beginTask("Line Cleaning: ", TOTAL_TICKS);
if (layers.size() > 1) {
monitor.subTask("Merging datasets");
} else {
monitor.subTask("Copying dataset to new location");
}
FeatureStore mergedFeatureStore = mergeLayers(outputFileNoExt);
layer.getMapInternal().getEditManagerInternal().commitTransaction();
if (monitor.isCanceled()) {
return;
}
if (cleanse) {
monitor.subTask("Data Preparation: ");
PerformCleansingAction action = new PerformCleansingAction(layer, pauseMonitor, mergedFeatureStore,
minimumLength, minimumCycleLength, nodeDistanceTolerance, areaTolerance, douglasPeuckerTolerance);
action.run(new SubProgressMonitor(monitor, CLEANSE_TICKS, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
}
if (monitor.isCanceled()) {
return;
}
if (clean) {
monitor.subTask("Light Conflation: ");
List<String> priorityOrder = new ArrayList<String>();
for (FeatureStore store : featureStores) {
priorityOrder.add(store.getSchema().getTypeName());
}
System.gc();
MinimumLengthProcessor mlp = new MinimumLengthProcessor(layer.getMapInternal(), mergedFeatureStore, 0);
mlp.setName("ZeroLength2");
mlp.run(new SubProgressMonitor(monitor, MIN_LENGTH_TICKS, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK), pauseMonitor);
mlp = null;
System.gc();
SimilarLinesProcessor cleaner = new SimilarLinesProcessor(layer.getMapInternal(), mergedFeatureStore,
samplingDistance, verySimilarTolerance, similarTolerance);
cleaner.setName("LineCleaner");
cleaner.run(new SubProgressMonitor(monitor, CLEAN_TICKS, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK), pauseMonitor);
}
layer.getMapInternal().getEditManagerInternal().commitTransaction();
if (monitor.isCanceled()) {
return;
}
if (cleanse) {
monitor.subTask("Final Cleaning: ");
System.gc();
MinimumLengthProcessor mlp = new MinimumLengthProcessor(layer.getMapInternal(), mergedFeatureStore, minimumLength);
mlp.setName("MinLength2");
mlp.run(new SubProgressMonitor(monitor, MIN_LENGTH_TICKS2, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK), pauseMonitor);
}
monitor.subTask("Saving");
layer.getMapInternal().getEditManagerInternal().commitTransaction();
monitor.worked(FINAL_COMMIT_TICKS);
monitor.done();
} catch( Throwable erp ) {
throw new InvocationTargetException( erp );
}
}
};
try {
getContainer().run( true, true, process );
long end=System.currentTimeMillis();
System.out.println("INFO: Time for operation="+(((double)end-start)/60000.0)+" min");
layer.setVisible(true);
} catch (InvocationTargetException e) {
((WizardPage) this.getContainer().getCurrentPage()).setErrorMessage(
e.getTargetException().getLocalizedMessage() );
e.printStackTrace();
return false;
} catch (InterruptedException e) {
this.getContainer().getCurrentPage().canFlipToNextPage(); // reset message
e.printStackTrace();
return false;
}
return true;
}
private List<FeatureStore> getFeatureStores() {
List<FeatureStore> featureStores = new ArrayList<FeatureStore>();
String[] selectedLayers = dataSelectionPage.layersList.getItems();
for (String layerName : selectedLayers) {
for (ILayer layer : dataSelectionPage.layers) {
if (layer.getName().equals(layerName)) {
try {
FeatureStore store = layer.getResource(FeatureStore.class, null);
featureStores.add(store);
} catch (IOException e) {
System.err.println("Cannot obtain FeatureStore from layer '"+layerName+"'");
e.printStackTrace();
}
}
}
}
return featureStores;
}
private void deleteFileIfExists(File file) throws IOException {
if (file.exists()) {
file.delete();
}
if (file.exists()) {
throw new IOException("Could not delete file " + file.getName());
}
}
private FeatureStore mergeLayers(String outputFileNoExt) throws IOException {
List<FeatureStore> storesToMerge = featureStores;
String shpFileName = outputFileNoExt + ".shp";
File shpFile = new File(shpFileName);
// TODO: maybe we should use a dialog to confirm overwriting a
// file? as it is, we only display a warning at the top
// of the wizard.
deleteFileIfExists(shpFile);
deleteFileIfExists(new File(outputFileNoExt + ".shx"));
deleteFileIfExists(new File(outputFileNoExt + ".dbf"));
deleteFileIfExists(new File(outputFileNoExt + ".fix"));
deleteFileIfExists(new File(outputFileNoExt + ".qix"));
try {
if ((shpFile.exists() && !shpFile.canWrite())
|| !shpFile.createNewFile()) {
throw new IOException("Cannot write to file " + shpFileName);
}
} catch (IOException e) {
throw (IOException) new IOException("Cannot write to file " + shpFileName).initCause(e);
}
URL shpFileURL;
try {
shpFileURL = shpFile.toURL();
} catch (MalformedURLException e) {
throw (IOException) new IOException().initCause(e);
}
if (storesToMerge.size() == 1) {
FeatureStore store = storesToMerge.iterator().next();
FeatureCollection fc = store.getFeatures();
IndexedShapefileDataStoreFactory factory = new IndexedShapefileDataStoreFactory();
ShapefileDataStore ds = (ShapefileDataStore)factory.createDataStore(shpFileURL);
ds.createSchema(fc.getSchema());
((FeatureStore) ds.getFeatureSource()).addFeatures(fc.reader());
} else {
try {
FeatureUtil.mergeFeatureStores(storesToMerge, shpFileURL);
} catch (Exception e) {
// TODO Handle Exception
throw (RuntimeException) new RuntimeException( ).initCause( e );
}
}
/*
* Turn the newly created Shapefile into a uDig resource
*/
List<IService> services = CatalogPlugin.getDefault().getServiceFactory().acquire(shpFileURL);
IService service = services.get(0);
CatalogPlugin.getDefault().getLocalCatalog().add(service);
List resources = service.members(null);
resource = (IGeoResource) resources.get(0);
Map map = ((Map)this.layers.get(0).getMap());
LayerFactory factory = map.getLayerFactory();
layer = factory.createLayer(resource);
layer.setVisible(false);
map.getLayersInternal().add(layer);
FeatureStore udigStore = layer.getResource(FeatureStore.class, null);
return udigStore;
}
}