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

Java tutorial

Introduction

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

Source

/* 
 * polymap.org
 * Copyright 2012, 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.Collections;
import java.util.HashMap;
import java.util.Map;

import java.lang.ref.SoftReference;

import javax.measure.unit.Unit;

import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.Feature;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.identity.FeatureId;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

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

import com.vividsolutions.jts.geom.Geometry;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Spinner;

import org.eclipse.rwt.graphics.Graphics;

import org.eclipse.jface.wizard.WizardPage;

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.operation.DefaultFeatureOperation;
import org.polymap.core.data.operation.FeatureOperationExtension;
import org.polymap.core.data.operation.IFeatureOperation;
import org.polymap.core.data.operation.IFeatureOperationContext;
import org.polymap.core.data.util.Geometries;
import org.polymap.core.model.security.ACLUtils;
import org.polymap.core.model.security.AclPermission;
import org.polymap.core.operation.OperationWizard;
import org.polymap.core.project.ILayer;
import org.polymap.core.project.ui.util.SimpleFormData;

/**
 * Buffer the geometries of the features.
 * 
 * @author <a href="http://www.polymap.de">Falko Brutigam</a>
 */
public class BufferFeaturesOperation extends DefaultFeatureOperation implements IFeatureOperation {

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

    private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null);

    private ILayer layer;

    private SoftReference<Map<FeatureId, Geometry>> undo;

    @Override
    public boolean init(IFeatureOperationContext _context) {
        super.init(_context);

        layer = (ILayer) context.getAdapter(ILayer.class);
        return layer != null && ACLUtils.checkPermission(layer, AclPermission.WRITE, false);
    }

    protected String i18n(String key, Object... args) {
        return Messages.get("BufferFeaturesOperation_" + key, args);
    }

    /**
     * 
     */
    @Override
    public Status execute(IProgressMonitor monitor) throws Exception {
        monitor.beginTask(context.adapt(FeatureOperationExtension.class).getLabel(), 100);

        final PipelineFeatureSource fs = (PipelineFeatureSource) context.featureSource();

        // wizard dialog
        monitor.subTask(i18n("userInput"));
        IUndoableOperation op = context.adapt(IUndoableOperation.class);
        OperationWizard wizard = new OperationWizard(op, context, monitor) {
            public boolean doPerformFinish() throws Exception {
                return true;
            }
        };
        final BufferInputPage bufferInputPage = new BufferInputPage();
        wizard.addPage(bufferInputPage);

        monitor.worked(10);

        // open wizard dialog
        if (OperationWizard.openDialog(wizard)) {
            monitor.worked(10);
            final IProgressMonitor submon = new SubProgressMonitor(monitor, 80,
                    SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);

            FeatureCollection features = context.features();
            CoordinateReferenceSystem crs = context.featureSource().getSchema().getCoordinateReferenceSystem();
            crs = crs != null ? crs : layer.getMap().getCRS();
            final Unit<?> uom = crs.getCoordinateSystem().getAxis(0).getUnit();

            int featuresSize = features.size();
            submon.beginTask(i18n("submonTitle", featuresSize), featuresSize);

            final HashMap<FeatureId, Geometry> undoMap = new HashMap<FeatureId, Geometry>();

            Transaction tx = new DefaultTransaction("BufferFeaturesOperation");
            fs.setTransaction(tx);

            FeatureIterator it = features.features();
            try {
                for (int count = 0; it.hasNext() && !submon.isCanceled(); count++) {
                    if (count % 100 == 0) {
                        submon.worked(100);
                        submon.subTask(String.valueOf(count));
                    }
                    Feature feature = it.next();
                    Geometry geom = (Geometry) feature.getDefaultGeometryProperty().getValue();
                    GeometryDescriptor geomDesc = feature.getDefaultGeometryProperty().getDescriptor();

                    undoMap.put(feature.getIdentifier(), geom);

                    if (uom.toString().equalsIgnoreCase("m")) {
                        geom = geom.buffer(bufferInputPage.distance, bufferInputPage.segments);
                    } else {
                        geom = Geometries.transform(geom, crs, Geometries.crs("EPSG:3857"));
                        geom = geom.buffer(bufferInputPage.distance, bufferInputPage.segments);
                        geom = Geometries.transform(geom, Geometries.crs("EPSG:3857"), crs);
                    }
                    fs.modifyFeatures(geomDesc, geom, ff.id(Collections.singleton(feature.getIdentifier())));
                }
                undo = new SoftReference(undoMap);
                tx.commit();
            } catch (Exception e) {
                tx.rollback();
                throw new RuntimeException(e);
            } finally {
                tx.close();
                it.close();
                submon.done();
            }

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

    @Override
    public boolean canUndo() {
        return undo != null && undo.get() != null;
    }

    @Override
    public Status undo(IProgressMonitor monitor) throws Exception {
        // XXX there is a race cond. between canUndo() and here
        Map<FeatureId, Geometry> undoMap = undo.get();

        monitor.beginTask(context.adapt(FeatureOperationExtension.class).getLabel(), undoMap.size());

        FeatureStore fs = (FeatureStore) context.featureSource();
        GeometryDescriptor geomDesc = fs.getSchema().getGeometryDescriptor();

        Transaction tx = new DefaultTransaction("BufferFeaturesOperation_undo");
        fs.setTransaction(tx);
        try {
            for (Map.Entry<FeatureId, Geometry> entry : undoMap.entrySet()) {
                if (monitor.isCanceled()) {
                    return Status.Cancel;
                }
                fs.modifyFeatures(geomDesc, entry.getValue(), ff.id(Collections.singleton(entry.getKey())));
                monitor.worked(1);
            }
            return Status.OK;
        } catch (Exception e) {
            tx.rollback();
            throw e;
        } finally {
            tx.close();
        }
    }

    @Override
    public boolean canRedo() {
        return true;
    }

    @Override
    public Status redo(IProgressMonitor monitor) throws Exception {
        return execute(monitor);
    }

    /**
     * A wizard page that gets buffer distance.
     */
    public class BufferInputPage extends WizardPage {

        public static final String ID = "BufferInputPage";

        private boolean mandatory = false;

        private double distance = 10;

        private int segments = 3;

        public BufferInputPage() {
            super(ID);
            setTitle(i18n("title"));
            setDescription(i18n("description"));
        }

        public void createControl(Composite parent) {
            Composite contents = new Composite(parent, SWT.NONE);
            FormLayout layout = new FormLayout();
            layout.spacing = 10;
            layout.marginWidth = 8;
            contents.setLayout(layout);
            setControl(contents);

            // buffer distance
            Composite dgroup = contents; //new Group( contents, SWT.NONE );
            //            dgroup.setText( i18n( "distanceGroupTitle" ) );
            //            dgroup.setLayoutData( SimpleFormData.filled().create() );
            //            layout = new FormLayout();
            //            layout.spacing = 10;
            //            layout.marginWidth = 8;
            //            dgroup.setLayout( layout );

            Label dl = new Label(dgroup, SWT.WRAP);
            dl.setText(i18n("distanceDescription"));
            dl.setForeground(Graphics.getColor(0x90, 0x90, 0x90));
            dl.setLayoutData(SimpleFormData.filled().width(200).bottom(-1).create());

            // distance
            Label dtl = new Label(dgroup, SWT.NONE);
            dtl.setText(i18n("distanceLabel"));
            dtl.setLayoutData(SimpleFormData.filled().top(dl, 2).bottom(-1).right(30).create());

            final Spinner dspinner = new Spinner(dgroup, SWT.BORDER);
            dspinner.setMinimum(-10000);
            dspinner.setMaximum(10000);
            dspinner.setSelection((int) distance);
            dspinner.setLayoutData(SimpleFormData.filled().top(dl).bottom(-1).left(dtl, 8).width(100).create());
            dspinner.addModifyListener(new ModifyListener() {
                public void modifyText(ModifyEvent ev) {
                    distance = dspinner.getSelection();
                }
            });

            // segments per quadrant
            Label ql = new Label(dgroup, SWT.NONE);
            ql.setText(i18n("quadrantLabel"));
            ql.setLayoutData(SimpleFormData.filled().top(dspinner, 2).bottom(-1).right(30).width(100).create());

            final Spinner qspinner = new Spinner(dgroup, SWT.BORDER);
            qspinner.setMinimum(0);
            qspinner.setMaximum(100);
            qspinner.setSelection(segments);
            qspinner.setLayoutData(SimpleFormData.filled().top(dspinner).bottom(-1).left(ql, 8).create());
            qspinner.addModifyListener(new ModifyListener() {
                public void modifyText(ModifyEvent ev) {
                    segments = qspinner.getSelection();
                }
            });

        }

        public boolean isPageComplete() {
            return !mandatory;
        }

        protected String i18n(String key, Object... args) {
            return Messages.get("BufferFeaturesOperation_BufferInputPage_" + key, args);
        }

    }

}