/* Spatial Operations & Editing Tools for uDig
*
* Axios Engineering under a funding contract with:
* Diputación Foral de Gipuzkoa, Ordenación Territorial
*
* http://b5m.gipuzkoa.net
* http://www.axios.es
*
* (C) 2006, Diputación Foral de Gipuzkoa, Ordenación Territorial (DFG-OT).
* DFG-OT agrees to license under Lesser General Public License (LGPL).
*
* You can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software
* Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package es.axios.udig.spatialoperations.ui.taskmanager;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import net.refractions.udig.catalog.IGeoResource;
import net.refractions.udig.project.ILayer;
import net.refractions.udig.project.IMap;
import org.eclipse.core.runtime.IProgressMonitor;
import org.geotools.data.FeatureStore;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.SchemaException;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.Id;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.vividsolutions.jts.geom.Geometry;
import es.axios.geotools.util.FeatureTypeUnionBuilder;
import es.axios.udig.spatialoperations.internal.i18n.Messages;
import es.axios.udig.spatialoperations.internal.parameters.ISpatialJoinGeomParameters;
import es.axios.udig.spatialoperations.internal.parameters.ISpatialJoinInExistentLayerParameters;
import es.axios.udig.spatialoperations.internal.parameters.ISpatialJoinInNewLayerParameters;
import es.axios.udig.spatialoperations.tasks.ISpatialJoinTask;
import es.axios.udig.spatialoperations.tasks.SpatialOperationException;
import es.axios.udig.spatialoperations.tasks.SpatialOperationFactory;
import es.axios.udig.spatialoperations.tasks.SpatialRelation;
import es.axios.udig.ui.commons.mediator.AppGISMediator;
import es.axios.udig.ui.commons.util.LayerUtil;
/**
* Monitoring of Spatial Join Operation.
* <p>
* This is an user interface that allows to see the progress of the spatial join
* and cancel it, if that is needed.
* </p>
*
*
* @author Mauricio Pazos (www.axios.es)
* @author Aritz Davila (www.axios.es)
*
*/
final class SpatialJoinMonitor extends SOTaskMonitor<Object> {
private static final Logger LOGGER = Logger.getLogger(SpatialJoinMonitor.class.getName());
private FeatureTypeUnionBuilder featureUnionBuilder = null;
private ISpatialJoinGeomParameters params = null;
private ILayer targetLayer = null;
private Future<Object> future;
private CoordinateReferenceSystem targetCrs;
/**
* New instance of ISOTaskMonitor
*
* @param params
* @return SOProcessMonitor
*/
public static ISOTaskMonitor newInstance(ISpatialJoinGeomParameters params) {
SpatialJoinMonitor monitor = new SpatialJoinMonitor(params);
return monitor;
}
private SpatialJoinMonitor(final ISpatialJoinGeomParameters params) {
this.params = params;
}
/**
* Creates the {@link SimpleFeatureType} required for the target store. The
* method will create the new type doing the union of attributes in first
* and second feature types.
*
* @param typeName
* @param firsType
* @param secondType
* @param geomClass
* @param crs
* @return FeatureType
* @throws SchemaException
*/
private SimpleFeatureType createJoinFeatureType(final String typeName,
final SimpleFeatureType firsType,
final SimpleFeatureType secondType,
final Class<? extends Geometry> geomClass,
final CoordinateReferenceSystem crs) throws SchemaException {
try {
// build the join feature type
this.featureUnionBuilder = new FeatureTypeUnionBuilder(typeName);
this.featureUnionBuilder.add(firsType).add(secondType).setGeometryClass("geometry", geomClass, crs); //$NON-NLS-1$
SimpleFeatureType newFeatureType = this.featureUnionBuilder.getFeatureType();
return newFeatureType;
} catch (SchemaException e) {
LOGGER.severe(e.getMessage());
throw e;
}
}
/**
* Initializes the join process
*/
@Override
protected void initTaskMonitor(IProgressMonitor progress) throws InvocationTargetException {
final Filter filterInFirstLayer = this.params.getFilterInFirstLayer();
final Filter filterInSecondLayer = this.params.getFilterInSecondLayer();
FeatureCollection<SimpleFeatureType, SimpleFeature> firstSource;
FeatureCollection<SimpleFeatureType, SimpleFeature> secondSource;
try {
firstSource = LayerUtil.getSelectedFeatures(this.params.getFirstLayer(), filterInFirstLayer);
secondSource = LayerUtil.getSelectedFeatures(this.params.getSecondLayer(), filterInSecondLayer);
} catch (IOException e) {
throw makeInitializeExcepion(progress, e);
}
final SpatialRelation spatialRelation = this.params.getSpatialRelation();
final CoordinateReferenceSystem mapCrs = this.params.getMapCrs();
final CoordinateReferenceSystem firstCrs = this.params.getFirstCRS();
final CoordinateReferenceSystem secondCrs = this.params.getSecondCRS();
progress.worked(1);
// creates the join geometry process
try {
progress.worked(2);
ISpatialJoinTask<Object> task;
if (this.params.isSelection()) {
task = SpatialOperationFactory.createSpatialJoinSelection(firstSource, secondSource, spatialRelation,
mapCrs, firstCrs, secondCrs);
} else {
SimpleFeatureStore targetStore = getTargetStore(firstSource, secondSource,
this.params, progress);
task = SpatialOperationFactory.createSpatialJoin(firstSource, secondSource, spatialRelation, mapCrs,
targetStore, firstCrs, secondCrs, targetCrs);
}
ExecutorService executor = Executors.newCachedThreadPool();
this.future = executor.submit(task);
progress.worked(3);
} catch (Exception e) {
throw makeInitializeExcepion(progress, e);
}
}
/**
* Creates the target {@link FeatureStore} doing the join of the feature
* types of the first and second source.
*
* @param firstSource
* @param secondSource
* @param params
* @param monito
* r
* @return {@link FeatureStore}
* @throws IOException
*/
private SimpleFeatureStore getTargetStore( FeatureCollection<SimpleFeatureType, SimpleFeature> firstSource,
FeatureCollection<SimpleFeatureType, SimpleFeature> secondSource,
ISpatialJoinGeomParameters params,
IProgressMonitor monitor)
throws IOException {
SimpleFeatureStore targetStore;
try {
if (params instanceof ISpatialJoinInExistentLayerParameters) {
targetLayer = ((ISpatialJoinInExistentLayerParameters) params).getTargetLayer();
targetStore = getFeatureStore(this.targetLayer);
targetCrs = ((ISpatialJoinInExistentLayerParameters) params).getTargetCRS();
} else {
// creates a new target layer
ISpatialJoinInNewLayerParameters p = (ISpatialJoinInNewLayerParameters) this.params;
// creates the join type for the new target store
SimpleFeatureType joinType = createJoinFeatureType(p.getTargetName(), firstSource.getSchema(),
secondSource.getSchema(), p.getTargetGeometry(), p.getMapCrs());
IMap map = this.params.getFirstLayer().getMap();
targetStore = (SimpleFeatureStore) createDataStore(map, joinType, monitor);
targetCrs = LayerUtil.getCrs(targetLayer);
}
return targetStore;
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* Creates the target {@link FeatureStore} which will be returned and the
* new {@link ILayer} that will be added in the current map.
*
* If the Spatial Join will return the features selected that fulfill the
* spatial relation then, it will return nothing.
*
* @param map
* @param featureType
* @param monitor
* @return a new FetureStore
* @throws SpatialOperationException
*/
private FeatureStore<SimpleFeatureType, SimpleFeature> createDataStore( final IMap map,
final SimpleFeatureType featureType,
final IProgressMonitor monitor)
throws SpatialOperationException {
if (this.params.isSelection()) {
return null;
}
IGeoResource geoResource = AppGISMediator.createTempGeoResource(featureType);
assert geoResource != null;
try {
FeatureStore<SimpleFeatureType, SimpleFeature> featureStore;
featureStore = geoResource.resolve(FeatureStore.class, monitor);
assert featureStore != null;
this.targetLayer = addLayerToMap(map, geoResource);
return featureStore;
} catch (IOException e) {
final String msg = e.getMessage();
throw new SpatialOperationException(msg);
}
}
/**
* Prepares the result of this process
*
* @throws InterruptedException
*/
@Override
protected void deliveryResult(Future<Object> future, ILayer target) throws InterruptedException {
try {
if (this.params.isSelection()) {
Id filter = (Id) future.get();
LayerUtil.presentSelection(this.params.getFirstLayer(), filter);
} else {
presentFeaturesOnTargetLayer(this.targetLayer);
}
} catch (Exception e) {
throw makeTaskInterrumptedException(getMonitor(), e);
}
}
@Override
protected String getBeginMessage() {
final String msg = MessageFormat.format("Spatial Join {0}.{1}.{2} ", params.getFirstLayer().getName(), params //$NON-NLS-1$
.getSpatialRelation().toString(), params.getSecondLayer().getName());
return msg;
}
@Override
protected String getCancelMessage() {
return Messages.SpatialJoinMonitor_canceled;
}
@Override
protected String getDoneMessage() {
return Messages.SpatialJoinMonitor_successful;
}
@Override
protected Future<Object> getFuture() {
return this.future;
}
@Override
protected IMap getMap() {
return this.params.getFirstLayer().getMap();
}
@Override
protected ILayer getTarget() {
return this.targetLayer;
}
}