org.polymap.core.data.operations.feature.CopyFeaturesOperation2.java Source code

Java tutorial

Introduction

Here is the source code for org.polymap.core.data.operations.feature.CopyFeaturesOperation2.java

Source

/*
 * polymap.org
 * Copyright 2011, Falko Brutigam. All rights reserved.
 *
 * This is free software; 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; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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 org.polymap.core.data.operations.feature;

import java.util.List;
import java.util.Properties;

import org.geotools.feature.FeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.identity.FeatureId;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;

import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.PageChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.wizard.IWizardPage;

import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;

import org.polymap.core.data.Messages;
import org.polymap.core.data.PipelineFeatureSource;
import org.polymap.core.data.feature.buffer.FeatureBufferProcessor;
import org.polymap.core.data.feature.typeeditor.AttributeMapping;
import org.polymap.core.data.feature.typeeditor.FeatureTypeEditorProcessor;
import org.polymap.core.data.feature.typeeditor.FeatureTypeEditorProcessorConfig;
import org.polymap.core.data.feature.typeeditor.FeatureTypeMapping;
import org.polymap.core.data.operation.DefaultFeatureOperation;
import org.polymap.core.data.operation.FeatureOperationExtension;
import org.polymap.core.data.operation.IFeatureOperation;
import org.polymap.core.data.operations.ChooseLayerPage;
import org.polymap.core.data.operations.NewFeatureOperation;
import org.polymap.core.data.pipeline.Pipeline;
import org.polymap.core.data.ui.featuretypeeditor.FeatureTypeEditor;
import org.polymap.core.data.ui.featuretypeeditor.ValueViewerColumn;
import org.polymap.core.data.util.Geometries;
import org.polymap.core.data.util.ProgressListenerAdaptor;
import org.polymap.core.data.util.RetypingFeatureCollection;
import org.polymap.core.model.security.ACLUtils;
import org.polymap.core.model.security.AclPermission;
import org.polymap.core.operation.OperationWizard;
import org.polymap.core.operation.OperationWizardPage;
import org.polymap.core.project.ILayer;
import org.polymap.core.project.ui.util.SimpleFormData;
import org.polymap.core.runtime.IMessages;
import org.polymap.core.runtime.Polymap;
import org.polymap.core.runtime.WeakListener;

/**
 * Copy features into another layer. The type of the features and the CRS might be
 * modified while copying.
 * 
 * @see NewFeatureOperation
 * @author <a href="http://www.polymap.de">Falko Brutigam</a>
 * @since 3.1
 */
public class CopyFeaturesOperation2 extends DefaultFeatureOperation implements IFeatureOperation {

    private static Log log = LogFactory.getLog(CopyFeaturesOperation2.class);

    private static IMessages i18n = Messages.forClass(CopyFeaturesOperation2.class);

    private PipelineFeatureSource source;

    private ILayer dest;

    private FeatureCollection features;

    private PipelineFeatureSource destFs;

    private List<FeatureId> createdFeatureIds;

    /** The copied features. */
    public FeatureCollection getFeatures() {
        return features;
    }

    public PipelineFeatureSource getDestFs() {
        return destFs;
    }

    public List<FeatureId> getCreatedFeatureIds() {
        return createdFeatureIds;
    }

    public Status execute(IProgressMonitor monitor) throws Exception {
        monitor.beginTask(context.adapt(FeatureOperationExtension.class).getLabel(), 10);

        source = (PipelineFeatureSource) context.featureSource();

        if (!(source.getSchema() instanceof SimpleFeatureType)) {
            throw new Exception(i18n.get("notSimpleType"));
        }

        // open wizard dialog
        monitor.subTask("Eingaben vom Nutzer...");
        IUndoableOperation op = context.adapt(IUndoableOperation.class);
        final OperationWizard wizard = new OperationWizard(op, context, monitor) {
            public boolean doPerformFinish() throws Exception {
                ((FeatureEditorPage2) getPage(FeatureEditorPage2.ID)).performFinish();
                return true;
            }
        };
        Polymap.getSessionDisplay().asyncExec(new Runnable() {
            public void run() {
                wizard.getShell().setMinimumSize(600, 600);
                wizard.getShell().layout(true);
            }
        });

        // ChooseLayerPage
        final ChooseLayerPage chooseLayerPage = new ChooseLayerPage(i18n.get("ChooseLayerPage_title"),
                i18n.get("ChooseLayerPage_description"), true);
        ILayer layer = context.adapt(ILayer.class);
        if (ACLUtils.checkPermission(layer, AclPermission.WRITE, false)) {
            chooseLayerPage.preset(layer);
        }
        chooseLayerPage.addFilter(new ViewerFilter() {
            @Override
            public boolean select(Viewer viewer, Object parentElm, Object elm) {
                if (elm instanceof ILayer) {
                    return ACLUtils.checkPermission((ILayer) elm, AclPermission.WRITE, false);
                }
                return true;
            }
        });
        wizard.addPage(chooseLayerPage);
        final FeatureEditorPage2 featureEditorPage = new FeatureEditorPage2();
        wizard.addPage(featureEditorPage);
        DirectCopyPage directCopyPage = new DirectCopyPage();
        wizard.addPage(directCopyPage);

        // get/set chosen layer
        wizard.addPageChangedListener(new IPageChangedListener() {
            public void pageChanged(PageChangedEvent ev) {
                log.info("Page: " + ev.getSelectedPage());
                if (featureEditorPage == ev.getSelectedPage()) {
                    dest = chooseLayerPage.getResult();
                }
            }
        });
        monitor.worked(1);

        // copy features
        if (OperationWizard.openDialog(wizard)) {
            final IProgressMonitor copyMonitor = new SubProgressMonitor(monitor, 8,
                    SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);

            destFs = PipelineFeatureSource.forLayer(dest, true);
            features = context.features();
            int featuresSize = features.size();

            copyMonitor.beginTask(i18n.get("taskTitle", featuresSize), featuresSize);

            // direct copy?
            if (directCopyPage.isDirectCopy()) {
                Pipeline pipe = destFs.getPipeline();
                Iterables.removeIf(pipe, Predicates.instanceOf(FeatureBufferProcessor.class));
            }

            // tranform schema
            features = featureEditorPage.retyped(features);

            // transform CRS
            SimpleFeatureType destSchema = destFs.getSchema();
            final CoordinateReferenceSystem destCrs = destSchema.getCoordinateReferenceSystem();
            SimpleFeatureType sourceSchema = (SimpleFeatureType) features.getSchema();
            // XXX do not use sourceSchema here as featureEditorPage ff. has set CRS to destFs schema CRS
            CoordinateReferenceSystem sourceCrs = sourceSchema.getCoordinateReferenceSystem();

            if (destCrs != null && !destCrs.equals(sourceCrs)) {
                // features = new ReprojectingFeatureCollection( features, destCrs );

                final MathTransform transform = Geometries.transform(sourceCrs, destCrs);
                final String geomName = sourceSchema.getGeometryDescriptor().getLocalName();

                // actually copy features; the above just sets attribute which is not supported by caching data sources
                final SimpleFeatureType retypedSchema = SimpleFeatureTypeBuilder.retype(sourceSchema, destCrs);
                features = new RetypingFeatureCollection(features, retypedSchema) {
                    protected Feature retype(Feature feature) {
                        try {
                            SimpleFeatureBuilder fb = new SimpleFeatureBuilder(retypedSchema);
                            fb.init((SimpleFeature) feature);
                            Geometry geom = (Geometry) feature.getProperty(geomName).getValue();
                            fb.set(geomName, JTS.transform(geom, transform));
                            return fb.buildFeature(feature.getIdentifier().getID());
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                };
            }

            // transform geometry types
            SimpleFeatureType featuresSchema = (SimpleFeatureType) features.getSchema();
            final GeometryDescriptor featureGeom = featuresSchema.getGeometryDescriptor();
            final GeometryDescriptor destGeom = destSchema.getGeometryDescriptor();
            if (featureGeom != null && destGeom != null
                    && !featureGeom.getType().getBinding().equals(destGeom.getType().getBinding())) {

                SimpleFeatureTypeBuilder ftb = new SimpleFeatureTypeBuilder();
                ftb.init(featuresSchema);
                ftb.remove(featureGeom.getLocalName());
                ftb.add(destGeom.getLocalName(), destGeom.getType().getBinding(),
                        destGeom.getCoordinateReferenceSystem());
                final SimpleFeatureType retypedSchema = ftb.buildFeatureType();

                features = new RetypingFeatureCollection(features, retypedSchema) {
                    protected Feature retype(Feature feature) {
                        try {
                            SimpleFeatureBuilder fb = new SimpleFeatureBuilder(retypedSchema);
                            fb.init((SimpleFeature) feature);
                            Geometry geom = (Geometry) feature.getProperty(featureGeom.getLocalName()).getValue();

                            // Point -> MultiPolygon
                            if (destGeom.getType().getBinding().equals(MultiPolygon.class)
                                    || destGeom.getType().getBinding().equals(Polygon.class)) {
                                geom = Geometries.transform(geom, destCrs, Geometries.crs("EPSG:3857"));
                                geom = geom.buffer(10, 3);
                                geom = Geometries.transform(geom, Geometries.crs("EPSG:3857"), destCrs);

                                fb.set(destGeom.getLocalName(), geom);
                                return fb.buildFeature(feature.getIdentifier().getID());
                            }
                            // Geometry -> anything
                            else if (geom instanceof Geometry) {
                                fb.set(destGeom.getLocalName(), geom);
                                return fb.buildFeature(feature.getIdentifier().getID());
                            } else {
                                throw new UnsupportedOperationException(
                                        "Unsupported geometry transformation: " + geom.getClass().getSimpleName()
                                                + " -> " + destGeom.getType().getBinding().getSimpleName());
                            }
                        } catch (RuntimeException e) {
                            throw e;
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                };
            }

            createdFeatureIds = destFs.addFeatures(features, new ProgressListenerAdaptor(copyMonitor));

            monitor.done();
            return Status.OK;
        }
        return Status.Cancel;
    }

    /**
     *
     */
    class FeatureEditorPage2 extends OperationWizardPage implements IWizardPage, IPageChangedListener {

        public static final String ID = "FeatureEditorPage2";

        private Composite content;

        private Properties configPageProps = new Properties();

        private FeatureTypeEditorProcessorConfig configPage;

        protected FeatureEditorPage2() {
            super(ID);
            setTitle(i18n.get("FeatureEditorPage_title"));
            setDescription(i18n.get("FeatureEditorPage_description"));
        }

        public void createControl(Composite parent) {
            this.content = new Composite(parent, SWT.NONE);
            FormLayout layout = new FormLayout();
            layout.spacing = 5;
            content.setLayout(layout);
            setControl(content);
            getWizard().addPageChangedListener(WeakListener.forListener(this));
        }

        public void pageChanged(PageChangedEvent ev) {
            log.info("pageChanged(): ev= " + ev.getSelectedPage());
            if (ev.getSelectedPage() == this /*&& editor == null*/) {
                pageEntered();
            }
        }

        protected void pageEntered() {
            getContainer().getShell().setMinimumSize(600, 600);
            getContainer().getShell().layout(true);

            setErrorMessage(null);

            // create default mapping
            FeatureTypeMapping mappings = new FeatureTypeMapping();
            SimpleFeatureType schema = null;
            try {
                PipelineFeatureSource fs = PipelineFeatureSource.forLayer(dest, true);
                schema = fs.getSchema();
            } catch (Exception e) {
                throw new RuntimeException(i18n.get("FeatureEditorPage_errorLayerSchema"), e);
            }

            SimpleFeatureType sourceSchema = source.getSchema();

            // check CRSs
            if (!CRS.equalsIgnoreMetadata(schema.getCoordinateReferenceSystem(),
                    sourceSchema.getCoordinateReferenceSystem())) {
                setMessage(i18n.get("FeatureEditorPage_errorCrs", CRS.toSRS(schema.getCoordinateReferenceSystem()),
                        CRS.toSRS(sourceSchema.getCoordinateReferenceSystem())), DialogPage.INFORMATION);
            }

            // geometry attribute
            GeometryDescriptor destGeomAttr = schema.getGeometryDescriptor();
            GeometryDescriptor sourceGeomAttr = sourceSchema.getGeometryDescriptor();
            if (destGeomAttr != null && sourceGeomAttr != null) {

                mappings.put(new AttributeMapping(destGeomAttr.getLocalName(),
                        // binding and CRS are tranformed in #execute()
                        sourceGeomAttr.getType().getBinding(), sourceGeomAttr.getCoordinateReferenceSystem(),
                        sourceGeomAttr.getLocalName(), null));

                // check geometry type
                Class destGeomType = destGeomAttr.getType().getBinding();
                Class sourceGeomType = sourceGeomAttr.getType().getBinding();
                if (!destGeomType.isAssignableFrom(sourceGeomType)) {
                    setMessage(i18n.get("FeatureEditorPage_errorGeom", destGeomType.getSimpleName(),
                            sourceGeomType.getSimpleName()), DialogPage.WARNING);
                }
            }

            for (AttributeDescriptor destAttr : schema.getAttributeDescriptors()) {
                if (destAttr == destGeomAttr) {
                    continue;
                }

                // find best matching attribut
                log.debug("Find mapping for: " + destAttr.getLocalName());
                AttributeDescriptor matchingAttr = null;
                int score = Integer.MAX_VALUE;

                for (AttributeDescriptor sourceAttr : sourceSchema.getAttributeDescriptors()) {

                    if (destAttr.getType().getBinding().isAssignableFrom(sourceAttr.getType().getBinding())) {

                        int s = StringUtils.getLevenshteinDistance(sourceAttr.getLocalName().toLowerCase(),
                                destAttr.getLocalName().toLowerCase());

                        log.debug("    check: " + sourceAttr.getLocalName() + ", score:" + s);
                        if (s < score) {
                            score = s;
                            matchingAttr = sourceAttr;
                            log.debug("    match: " + matchingAttr.getLocalName() + " (" + score + ")");
                        }
                    }
                }
                if (matchingAttr != null) {
                    mappings.put(new AttributeMapping(destAttr.getLocalName(), destAttr.getType().getBinding(),
                            null, matchingAttr.getLocalName(), null));
                }
            }
            configPageProps.put("mappings", mappings.serialize());

            if (configPage != null) {
                configPage.getControl().dispose();
            }
            configPage = new FeatureTypeEditorProcessorConfig();
            configPage.init(dest, configPageProps);
            configPage.setSourceFeatureType(sourceSchema);

            configPage.createControl(content);
            configPage.getControl().setLayoutData(new SimpleFormData().fill().create());
            content.layout(true);

            getContainer().updateButtons();
        }

        public RetypingFeatureCollection retyped(FeatureCollection src) {
            final FeatureTypeEditorProcessor processor = new FeatureTypeEditorProcessor();
            processor.init(configPageProps);
            final SimpleFeatureBuilder builder = new SimpleFeatureBuilder(
                    (SimpleFeatureType) processor.getFeatureType());

            return new RetypingFeatureCollection(src, null) {
                @Override
                public SimpleFeatureType getSchema() {
                    return (SimpleFeatureType) processor.getFeatureType();
                }

                @Override
                protected Feature retype(Feature feature) {
                    try {
                        // make new FID to avoid problem when copying features of the same layer
                        return processor.transformFeature((SimpleFeature) feature, builder, null);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            };
        }

        public void performFinish() {
            log.info("performFinish(): ...");

            configPage.performOk();
            //
            //            PipelineProcessor proc = source.getPipeline().get( 0 );
            //            if (!(proc instanceof FeatureTypeEditorProcessor)) {
            //                proc = new FeatureTypeEditorProcessor();
            //                source.getPipeline().addFirst( proc );
            //            }
            //            ((FeatureTypeEditorProcessor)proc).init( configPageProps );
            //
            //            // give FeatureTypeEditorProcessor the upstream FeatureType
            //            SimpleFeatureType schema = source.getSchema();
            //            log.debug( "Schema: " + schema );
        }

        public boolean isPageComplete() {
            if (getErrorMessage() != null) {
                return false;
            }
            return configPage != null ? configPage.okToLeave() : false;
        }

    }

    //    /**
    //     * Adapt geometry types:
    //     * <ul>
    //     * <li>Any -> Point: </li>
    //     * </ul> 
    //     */
    //    class GeometryAdaptingFeatureCollection
    //            extends RetypingFeatureCollection<SimpleFeatureType,SimpleFeature> {
    //
    //        Class<? extends Geometry>       targetType;
    //        
    //
    //        public GeometryAdaptingFeatureCollection( FeatureCollection delegate, SimpleFeatureType targetSchema ) {
    //            super( delegate, targetSchema );
    //            GeometryDescriptor geomDescr = targetSchema.getGeometryDescriptor();
    //            targetType = (Class<? extends Geometry>)(geomDescr != null ? geomDescr.getType().getBinding() : null);
    //            
    //            SimpleFeatureTypeBuilder ftp = new SimpleFeatureTypeBuilder();
    //            ftp.init( targetSchema );
    //            
    //            JTS.
    //        }
    //
    //
    //
    //        protected SimpleFeature retype( SimpleFeature input ) {
    //            GeometryAttribute prop = input.getDefaultGeometryProperty();
    //            if (targetType != null && prop != null) {
    //                // same type?
    //                if (prop.getType().getBinding() == targetType) {
    //                    return input;
    //                }
    //                // different types
    //                else {
    //                    new
    //                }
    //            }
    //            else {
    //                return input;
    //            }
    //        }
    //        
    //    }

    /**
     * @deprecated
     */
    class FeatureEditorPage extends OperationWizardPage implements IWizardPage, IPageChangedListener {

        public static final String ID = "FeatureEditorPage";

        private FeatureTypeEditor editor;

        private Composite contents;

        protected FeatureEditorPage() {
            super(ID);
            setTitle(i18n.get("FeatureEditorPage_title"));
            setDescription(i18n.get("FeatureEditorPage_description"));
        }

        public void createControl(Composite parent) {
            this.contents = new Composite(parent, SWT.BORDER);
            FormLayout layout = new FormLayout();
            layout.spacing = 5;
            contents.setLayout(layout);
            setControl(contents);
            getWizard().addPageChangedListener(WeakListener.forListener(this));
        }

        public void pageChanged(PageChangedEvent ev) {
            log.info("pageChanged(): ev= " + ev.getSelectedPage());
            if (ev.getSelectedPage() == this && editor == null) {
                getContainer().getShell().setMinimumSize(SWT.DEFAULT, 600);
                getContainer().getShell().layout(true);

                editor = new FeatureTypeEditor();

                FeatureTypeMapping mapping = new FeatureTypeMapping();
                editor.addViewerColumn(new ValueViewerColumn(mapping, source.getSchema()));

                try {
                    PipelineFeatureSource fs = PipelineFeatureSource.forLayer(dest, true);
                    editor.createTable(contents, new SimpleFormData().fill().create(), fs.getSchema(), true);
                    contents.layout(true);
                } catch (Exception e) {
                    throw new RuntimeException(i18n.get("FeatureEditorPage_errorLayerSchema"), e);
                }
            }
        }

        public boolean isPageComplete() {
            return true;
        }

    }

    class DirectCopyPage extends OperationWizardPage implements IWizardPage, IPageChangedListener {

        public static final String ID = "DirectCopyPage";

        private Composite contents;

        private boolean isDirectCopy;

        protected DirectCopyPage() {
            super(ID);
            setTitle(i18n.get("DirectCopyPage_title"));
            setDescription(i18n.get("DirectCopyPage_description"));
        }

        public boolean isDirectCopy() {
            return isDirectCopy;
        }

        public void createControl(Composite parent) {
            this.contents = new Composite(parent, SWT.NONE);
            FormLayout layout = new FormLayout();
            layout.spacing = 5;
            layout.marginWidth = layout.marginHeight = 20;
            contents.setLayout(layout);
            setControl(contents);
            getWizard().addPageChangedListener(WeakListener.forListener(this));

            final Button check = new Button(contents, SWT.CHECK);
            check.setText(i18n.get("DirectCopyPage_text"));
            check.addSelectionListener(new SelectionAdapter() {
                public void widgetSelected(SelectionEvent e) {
                    isDirectCopy = check.getSelection();
                }
            });
        }

        @Override
        public void pageChanged(PageChangedEvent ev) {
            log.info("ev: " + ev);
        }

    }

}