org.polymap.core.data.operations.NewFeatureOperation.java Source code

Java tutorial

Introduction

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

Source

/* 
 * polymap.org
 * Copyright 2009-2013, Polymap GmbH. 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;

import java.io.IOException;
import java.io.StringReader;

import org.geotools.data.FeatureStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.geojson.feature.FeatureJSON;
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.FeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.Id;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;

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

import com.google.common.collect.Iterables;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Polygon;

import org.eclipse.swt.widgets.Display;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.AbstractOperation;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;

import org.polymap.core.data.DataPlugin;
import org.polymap.core.data.Messages;
import org.polymap.core.data.PipelineFeatureSource;
import org.polymap.core.data.feature.FidSet;
import org.polymap.core.data.feature.buffer.LayerFeatureBufferManager;
import org.polymap.core.geohub.LayerFeatureSelectionManager;
import org.polymap.core.project.ILayer;
import org.polymap.core.runtime.Polymap;
import org.polymap.core.workbench.PolymapWorkbench;

/**
 * Creates a new feature. This operation is triggered by
 * {@link DrawFeatureEditorAction} but might be used by other classes as well.
 * <p/>
 * Undo is implemented by simply removing the newly created feature. The
 * {@link LayerFeatureBufferManager} is able to revert changes for one feature,
 * however this would not work for features of an EntitySourceProcessor.
 * 
 * @author <a href="http://www.polymap.de">Falko Brutigam</a>
 * @since 3.0
 */
public class NewFeatureOperation extends AbstractOperation implements IUndoableOperation {

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

    private ILayer layer;

    private FeatureStore fs;

    private String jsonParam;

    /** The fids created on last execute() call. */
    private FidSet fids;

    /**
     * Create a new feature from the given JSON string.
     * <p>
     * Any exceptions while parsing, creating or adding is thrown by the
     * {@link #execute(IProgressMonitor, IAdaptable)} method.
     *  
     * @param layer
     * @param jsonParam
     */
    public NewFeatureOperation(ILayer layer, FeatureStore fs, String json) {
        super(Messages.get("NewFeatureOperation_label", layer.getLabel()));
        this.layer = layer;
        this.jsonParam = json;
    }

    /**
     * Create a new, empty feature.
     * <p>
     * Any exceptions while parsing, creating or adding is thrown by the
     * {@link #execute(IProgressMonitor, IAdaptable)} method.
     *  
     * @param layer
     * @param jsonParam
     */
    public NewFeatureOperation(ILayer layer, FeatureStore fs) {
        super(Messages.get("NewFeatureOperation_label", layer.getLabel()));
        this.layer = layer;
    }

    /**
     * Returns the feature after the operation was executed.
     * @throws IOException 
     */
    public Feature getCreatedFeature() throws IOException {
        Id fidFilter = DataPlugin.ff.id(fids);
        FeatureCollection coll = fs.getFeatures(fidFilter);
        return (Feature) coll.toArray(new Feature[1])[0];
    }

    public ILayer getLayer() {
        return layer;
    }

    public FeatureStore getFeatureStore() {
        return fs;
    }

    public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
        try {
            // display and monitor
            Display display = Polymap.getSessionDisplay();
            log.debug("### Display: " + display);
            monitor.subTask(getLabel());

            // FeatureStore for layer
            if (fs == null) {
                fs = PipelineFeatureSource.forLayer(layer, true);
            }
            SimpleFeatureType schema = (SimpleFeatureType) fs.getSchema();

            // create feature
            SimpleFeature newFeature;
            if (jsonParam != null) {
                newFeature = parseJSON(schema, jsonParam);
            } else {
                SimpleFeatureBuilder fb = new SimpleFeatureBuilder(schema);
                newFeature = fb.buildFeature(null);
            }

            // add feature
            FeatureCollection<SimpleFeatureType, SimpleFeature> features = FeatureCollections.newCollection();
            features.add(newFeature);

            fids = new FidSet(fs.addFeatures(features));
            log.info("### Feature created: " + fids);

            monitor.worked(1);

            // update UI
            display.asyncExec(new Runnable() {
                public void run() {
                    try {
                        // hover event
                        LayerFeatureSelectionManager fsm = LayerFeatureSelectionManager.forLayer(layer);
                        fsm.setHovered(Iterables.getFirst(fids, null).getID());
                    } catch (Exception e) {
                        PolymapWorkbench.handleError(DataPlugin.PLUGIN_ID, this, e.getMessage(), e);
                    }
                }
            });
            return Status.OK_STATUS;
        } catch (Exception e) {
            throw new ExecutionException(Messages.get("NewFeatureOperation_errorMsg"), e);
        }
    }

    protected SimpleFeature parseJSON(FeatureType schema, String json) throws Exception {
        // parse JSON
        log.debug(json);
        FeatureJSON io = new FeatureJSON();
        SimpleFeature feature = io.readFeature(new StringReader(json));
        log.debug("JSON Feature: " + feature);

        // builder
        SimpleFeatureBuilder fb = new SimpleFeatureBuilder((SimpleFeatureType) schema);
        // XXX do we need this anyway?
        //        for (Property prop : feature.getProperties()) {
        //            if (!(prop instanceof GeometryAttribute)) {
        //                featureBuilder.set( prop.getName(), prop.getValue() );
        //            }
        //        }

        // convert to multi geometry if necessary
        GeometryDescriptor geomDesc = schema.getGeometryDescriptor();
        GeometryFactory gf = new GeometryFactory();
        String geomName = geomDesc.getType().getName().toString();
        log.debug("    geometry: " + geomName);
        Geometry geom = (Geometry) feature.getDefaultGeometry();
        if ("MultiLineString".equals(geomName)) {
            log.debug("    convert geometry: " + geom + " -> " + geomName);
            geom = gf.createMultiLineString(new LineString[] { (LineString) geom });
        } else if ("MultiPolygon".equals(geomName)) {
            log.debug("    convert geometry: " + geom + " -> " + geomName);
            geom = gf.createMultiPolygon(new Polygon[] { (Polygon) geom });
        }

        // transform CRS
        CoordinateReferenceSystem layerCRS = schema.getCoordinateReferenceSystem();
        CoordinateReferenceSystem mapCRS = layer.getMap().getCRS();
        if (!mapCRS.equals(layerCRS)) {
            //log.debug( "    transforming geom: " + mapCRS.getName() + " -> " + layerCRS.getName() );
            MathTransform transform = CRS.findMathTransform(mapCRS, layerCRS);
            geom = JTS.transform(geom, transform);
        }

        // create feature
        fb.set(geomDesc.getLocalName(), geom);
        return fb.buildFeature(null);
    }

    public boolean canUndo() {
        return true;
    }

    @Override
    public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
        try {
            fs.removeFeatures(DataPlugin.ff.id(fids));
            monitor.worked(1);
            return Status.OK_STATUS;
        } catch (Exception e) {
            throw new ExecutionException(Messages.get("NewFeatureOperation_errorMsg"), e);
        }
    }

    public boolean canRedo() {
        return true;
    }

    public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
        return execute(monitor, info);
    }

}