001: /* uDig - User Friendly Desktop Internet GIS client
002: * http://udig.refractions.net
003: * (C) 2004, Refractions Research Inc.
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation;
008: * version 2.1 of the License.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: */
015: package net.refractions.udig.project.internal.commands.edit;
016:
017: import java.awt.Rectangle;
018: import java.io.IOException;
019: import java.util.HashMap;
020: import java.util.HashSet;
021: import java.util.Iterator;
022: import java.util.Map;
023: import java.util.Set;
024:
025: import net.refractions.udig.core.internal.FeatureUtils;
026: import net.refractions.udig.project.ILayer;
027: import net.refractions.udig.project.command.AbstractCommand;
028: import net.refractions.udig.project.command.NavCommand;
029: import net.refractions.udig.project.command.UndoableMapCommand;
030: import net.refractions.udig.project.command.factory.NavigationCommandFactory;
031: import net.refractions.udig.project.internal.Layer;
032: import net.refractions.udig.project.internal.Messages;
033: import net.refractions.udig.project.internal.ProjectPlugin;
034:
035: import org.eclipse.core.runtime.IProgressMonitor;
036: import org.eclipse.core.runtime.SubProgressMonitor;
037: import org.geotools.data.DefaultQuery;
038: import org.geotools.data.FeatureSource;
039: import org.geotools.data.FeatureStore;
040: import org.geotools.data.Query;
041: import org.geotools.feature.Feature;
042: import org.geotools.feature.FeatureCollection;
043: import org.geotools.feature.FeatureIterator;
044: import org.geotools.feature.FeatureType;
045: import org.geotools.feature.collection.AbstractFeatureCollection;
046: import org.geotools.filter.FidFilter;
047: import org.geotools.filter.Filter;
048: import org.geotools.filter.FilterFactoryFinder;
049: import org.geotools.geometry.jts.JTS;
050: import org.geotools.referencing.CRS;
051: import org.geotools.referencing.crs.DefaultGeographicCRS;
052: import org.opengis.referencing.FactoryException;
053: import org.opengis.referencing.crs.CoordinateReferenceSystem;
054: import org.opengis.referencing.operation.MathTransform;
055: import org.opengis.referencing.operation.TransformException;
056:
057: import com.vividsolutions.jts.geom.Envelope;
058:
059: /**
060: * Copies features selected by the filter from the source layer to the destination layer. Then sets
061: * the filter of the destination layer to be the newly added features.
062: *
063: * @author jones
064: * @since 1.1.0
065: */
066: public class CopyFeaturesCommand extends AbstractCommand implements
067: UndoableMapCommand {
068:
069: private Layer destinationLayer;
070: private Filter filter;
071: private ILayer sourceLayer;
072:
073: // for undo
074: private FidFilter addedFeaturesFilter;
075: private Filter previousDesinationLayerFilter;
076: private Envelope previousEnvelope;
077:
078: public CopyFeaturesCommand(ILayer sourceLayer, Filter filter,
079: ILayer destinationLayer) {
080: this .sourceLayer = sourceLayer;
081: this .filter = filter;
082: this .destinationLayer = (Layer) destinationLayer;
083: }
084:
085: public void run(IProgressMonitor monitor) throws Exception {
086: if (sourceLayer == null || destinationLayer == null)
087: return;
088: previousEnvelope = getMap().getViewportModel().getBounds();
089: previousDesinationLayerFilter = destinationLayer.getFilter();
090: copyFeatures(sourceLayer, filter, destinationLayer, monitor);
091: }
092:
093: @SuppressWarnings("unchecked")
094: private void copyFeatures(ILayer sourceLayer, Filter filter,
095: final Layer targetLayer, final IProgressMonitor monitor) {
096: monitor.beginTask(Messages.CopyFeaturesCommand_name, 104);
097: final int[] worked = new int[] { -3 };
098: monitor.worked(1);
099: try {
100: SubProgressMonitor subProgressMonitor = new SubProgressMonitor(
101: monitor, 2);
102: FeatureStore destination = targetLayer.getResource(
103: FeatureStore.class, subProgressMonitor);
104: subProgressMonitor.done();
105: worked[0] += 2;
106: subProgressMonitor = new SubProgressMonitor(monitor, 2);
107: FeatureSource source = sourceLayer.getResource(
108: FeatureSource.class, subProgressMonitor);
109: subProgressMonitor.done();
110: worked[0] += 2;
111: // If no FeatureStore then features can't be copied
112: // If no FeatureSource then features can't be copied
113: if (destination == null || source == null) {
114: targetLayer.setFilter(filter);
115: return;
116: }
117:
118: // Create a query that will get the attributes that are compatible
119: // what is compatible? Do the attribute names and types have to be the same or
120: // just the types.
121: // Executive decision:
122: // Match both name and type, the rest must be customized.
123: final HashMap<String, String> attributeMap = new HashMap<String, String>();
124: Query query = createQuery(sourceLayer, filter, targetLayer,
125: attributeMap);
126: if (attributeMap.isEmpty()) {
127: targetLayer.setFilter(filter);
128: return;
129: }
130: MathTransform mt = createMathTransform(sourceLayer,
131: targetLayer);
132: FeatureCollection features = source.getFeatures(query);
133: FeatureType schema = targetLayer.getSchema();
134:
135: CopyFeatureCollection c = new CopyFeatureCollection(schema,
136: features, monitor, worked, mt, attributeMap,
137: targetLayer.layerToMapTransform());
138: Envelope env = c.env;
139: targetLayer.eSetDeliver(false);
140: try {
141: Set<String> fids = destination.addFeatures(c);
142:
143: displayCopiedFeatures(env);
144:
145: addedFeaturesFilter = FilterFactoryFinder
146: .createFilterFactory().createFidFilter();
147: addedFeaturesFilter.addAllFids(fids);
148: targetLayer.setFilter(addedFeaturesFilter);
149: } finally {
150: targetLayer.eSetDeliver(true);
151: }
152: getMap().getRenderManager().refresh(targetLayer, env);
153: } catch (IOException e) {
154: throw (RuntimeException) new RuntimeException()
155: .initCause(e);
156: } finally {
157: monitor.done();
158: }
159: }
160:
161: private void displayCopiedFeatures(Envelope env) {
162: if (!getMap().getViewportModel().getBounds().contains(env)
163: && !env.isNull() || tooSmallOnScreen(env)) {
164:
165: double d = env.getHeight() / 2;
166: double e = env.getWidth() / 2;
167: NavCommand command = NavigationCommandFactory.getInstance()
168: .createSetViewportBBoxCommand(
169: new Envelope(env.getMinX() - e, env
170: .getMaxX()
171: + e, env.getMinY() - d, env
172: .getMaxY()
173: + d),
174: getMap().getViewportModel().getCRS());
175:
176: getMap().sendCommandSync(command);
177: }
178: }
179:
180: private MathTransform createMathTransform(ILayer sourceLayer,
181: ILayer targetLayer) {
182: MathTransform temp;
183: try {
184: CoordinateReferenceSystem targetCRS = targetLayer.getCRS();
185: CoordinateReferenceSystem sourceCRS = sourceLayer.getCRS();
186: if (targetCRS.equals(sourceCRS))
187: temp = null;
188: else
189: temp = CRS
190: .findMathTransform(sourceCRS, targetCRS, true);
191: if (temp == null || temp.isIdentity())
192: temp = null;
193: } catch (FactoryException e1) {
194: ProjectPlugin.log("", e1); //$NON-NLS-1$
195: temp = null;
196: }
197:
198: if (temp == null) {
199: try {
200: return CRS.findMathTransform(
201: DefaultGeographicCRS.WGS84,
202: DefaultGeographicCRS.WGS84);
203: } catch (Exception e) {
204: ProjectPlugin.log("", e); //$NON-NLS-1$
205: return null;
206: }
207: }
208: return temp;
209: }
210:
211: boolean tooSmallOnScreen(Envelope env) {
212: double[] d = new double[] { env.getMinX(), env.getMinY(),
213: env.getMaxX(), env.getMaxY() };
214: getMap().getViewportModel().worldToScreenTransform().transform(
215: d, 0, d, 0, 2);
216: Rectangle r = new Rectangle((int) d[0], (int) d[1], (int) Math
217: .abs(d[2] - d[0]), (int) Math.abs(d[3] - d[1]));
218: return (double) r.getWidth() < getMap().getRenderManager()
219: .getMapDisplay().getWidth()
220: / (double) 16
221: && (double) r.getHeight() < getMap().getRenderManager()
222: .getMapDisplay().getHeight()
223: / (double) 16;
224: }
225:
226: public static int updateProgress(final IProgressMonitor monitor,
227: int numberToCopyForIncrement, final int[] worked) {
228: int result = 0;
229: if (numberToCopyForIncrement < 1) {
230: monitor.worked(1);
231: worked[0]++;
232: int i = 100 / (100 - worked[0]);
233: result = 1000 * i;
234: } else {
235: result = numberToCopyForIncrement - 1;
236: }
237:
238: return result;
239: }
240:
241: /**
242: * Creates a query that requests the features in sourceLayer as dictated by filter. Query only
243: * requests the attributes that can be mapped from sourceLayer to targetLayer.
244: *
245: * @param queryAttributes populates with a mapping of attributeTypeNames from targetLayer to
246: * sourcelayer
247: * @return
248: */
249: @SuppressWarnings("unchecked")
250: private Query createQuery(ILayer sourceLayer, Filter filter,
251: Layer targetLayer, Map<String, String> queryAttributes) {
252: FeatureType sourceSchema = sourceLayer.getSchema();
253: FeatureType targetSchema = targetLayer.getSchema();
254: // Maps type names to type names since we are ignoring case
255:
256: queryAttributes.putAll(FeatureUtils.createAttributeMapping(
257: sourceSchema, targetSchema));
258: Set<String> properties = new HashSet(queryAttributes.values());
259: return new DefaultQuery(sourceSchema.getTypeName(), filter,
260: properties.toArray(new String[properties.size()]));
261: }
262:
263: public String getName() {
264: return Messages.CopyFeaturesCommand_name;
265: }
266:
267: public void rollback(IProgressMonitor monitor) throws Exception {
268: if (sourceLayer == null || destinationLayer == null)
269: return;
270: monitor.beginTask(
271: Messages.CopyFeaturesCommand_undo + getName(), 4);
272: monitor.worked(1);
273: this .destinationLayer.eSetDeliver(false);
274: try {
275: FeatureStore store = this .destinationLayer.getResource(
276: FeatureStore.class, new SubProgressMonitor(monitor,
277: 1));
278: store.removeFeatures(addedFeaturesFilter);
279: this .destinationLayer
280: .setFilter(previousDesinationLayerFilter);
281: } finally {
282: this .destinationLayer.eSetDeliver(true);
283: }
284: getMap().getViewportModelInternal().setBounds(
285: this .previousEnvelope);
286: }
287:
288: private static class CopyFeatureCollection extends
289: AbstractFeatureCollection {
290:
291: final FeatureType schema;
292: final FeatureCollection features;
293: final IProgressMonitor monitor;
294: final int[] worked;
295: final MathTransform mt, toWorld;
296: final Map<String, String> attributeMap;
297: final Envelope env;
298:
299: CopyFeatureCollection(FeatureType schema,
300: FeatureCollection features, IProgressMonitor monitor,
301: int[] worked, MathTransform mt,
302: HashMap<String, String> attributeMap,
303: MathTransform toWorld) {
304: super (schema);
305: this .schema = schema;
306: this .features = features;
307: this .monitor = monitor;
308: this .worked = worked;
309: this .mt = mt;
310: this .attributeMap = attributeMap;
311: this .env = new Envelope();
312: this .toWorld = toWorld;
313: }
314:
315: @Override
316: public int size() {
317: return features.size();
318: }
319:
320: Map<Iterator, FeatureIterator> iterators = new HashMap<Iterator, FeatureIterator>();
321:
322: @Override
323: protected Iterator openIterator() {
324: final FeatureIterator iter = features.features();
325: Iterator i = new Iterator() {
326:
327: // for progress monitor.
328: int numberToCopyForIncrement = 1000;
329: private Feature next;
330: Iterator<Feature> copiedFeatures;
331:
332: public Feature next() {
333: Feature result = next;
334: next = null;
335: return result;
336: }
337:
338: public boolean hasNext() {
339: while (next == null) {
340: if (copiedFeatures != null
341: && copiedFeatures.hasNext()) {
342: next = copiedFeatures.next();
343: } else {
344: if (!iter.hasNext())
345: return false;
346: Feature source = iter.next();
347: numberToCopyForIncrement = updateProgress(
348: monitor, numberToCopyForIncrement,
349: worked);
350: copiedFeatures = FeatureUtils.copyFeature(
351: source, schema, attributeMap, mt)
352: .iterator();
353: if (!copiedFeatures.hasNext())
354: return false;
355: next = copiedFeatures.next();
356: Envelope newbounds = next.getBounds();
357: try {
358: newbounds = JTS.transform(newbounds,
359: toWorld);
360: if (env.isNull()) {
361: env.init(newbounds);
362: } else {
363: env.expandToInclude(newbounds);
364: }
365: } catch (TransformException e) {
366: ProjectPlugin.log("", e); //$NON-NLS-1$
367: }
368:
369: }
370: }
371: return next != null;
372: }
373:
374: public void remove() {
375: throw new UnsupportedOperationException();
376: }
377:
378: };
379: iterators.put(i, iter);
380: return i;
381: }
382:
383: @Override
384: protected void closeIterator(Iterator close) {
385: iterators.get(close).close();
386: }
387: }
388:
389: }
|