Package org.locationtech.geogig.api.plumbing.merge

Source Code of org.locationtech.geogig.api.plumbing.merge.MergeFeaturesOp

/* Copyright (c) 2013-2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Victor Olaya (Boundless) - initial implementation
*/
package org.locationtech.geogig.api.plumbing.merge;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.locationtech.geogig.api.AbstractGeoGigOp;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.RevFeature;
import org.locationtech.geogig.api.RevFeatureType;
import org.locationtech.geogig.api.plumbing.RevObjectParse;
import org.locationtech.geogig.api.plumbing.diff.GeometryAttributeDiff;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.PropertyDescriptor;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.vividsolutions.jts.geom.Geometry;

/**
* This operation merges two features that have compatible changes, returning the result of this
* automatic merging. Features must have the same schema
*
* No checking is performed to see that changes are actually compatible, so this should be done in
* advance. If that's not the case, the merged result might have lost some changes made on one of
* the features to merge, which will be overwritten by changes in the other one
*
*/
public class MergeFeaturesOp extends AbstractGeoGigOp<Feature> {

    private NodeRef nodeRefB;

    private NodeRef nodeRefA;

    private NodeRef ancestorRef;

    @Override
    protected  Feature _call() {
        checkNotNull(nodeRefA, "first feature version not specified");
        checkNotNull(nodeRefB, "second feature version not specified");
        checkNotNull(ancestorRef, "ancestor version not specified");
        // String firstPath = removeRef(oldNodeRef.path());
        // String newPath = removeRef(newNodeRef.path());
        checkArgument(nodeRefA.path().equals(nodeRefB.path()),
                "old and new versions do not correspond to the same feature");

        Optional<RevFeature> featureA = command(RevObjectParse.class).setObjectId(
                nodeRefA.getNode().getObjectId()).call(RevFeature.class);
        checkArgument(featureA.isPresent(), "Invalid reference: %s", nodeRefA);

        Optional<RevFeature> featureB = command(RevObjectParse.class).setObjectId(
                nodeRefB.getNode().getObjectId()).call(RevFeature.class);
        checkArgument(featureB.isPresent(), "Invalid reference: %s", nodeRefB);

        Optional<RevFeature> ancestorFeature = command(RevObjectParse.class).setObjectId(
                ancestorRef.getNode().getObjectId()).call(RevFeature.class);
        checkArgument(ancestorFeature.isPresent(), "Invalid reference: %s", ancestorRef);

        Optional<RevFeatureType> featureTypeA = command(RevObjectParse.class).setObjectId(
                nodeRefA.getMetadataId()).call(RevFeatureType.class);
        checkArgument(featureTypeA.isPresent(), "Invalid reference: %s", nodeRefA);

        Optional<RevFeatureType> featureTypeB = command(RevObjectParse.class).setObjectId(
                nodeRefB.getMetadataId()).call(RevFeatureType.class);
        checkArgument(featureTypeB.isPresent(), "Invalid reference: %s", nodeRefB);

        Optional<RevFeatureType> ancestorFeatureType = command(RevObjectParse.class).setObjectId(
                ancestorRef.getMetadataId()).call(RevFeatureType.class);
        checkArgument(ancestorFeatureType.isPresent(), "Invalid reference: %s", ancestorRef);

        Preconditions.checkArgument(
                featureTypeA.equals(featureTypeB) && featureTypeA.equals(ancestorFeatureType),
                "Non-matching feature types. Cannot merge");

        return merge(featureA.get(), featureB.get(), ancestorFeature.get(),
                ancestorFeatureType.get());

    }

    @SuppressWarnings("unchecked")
    private Feature merge(RevFeature featureA, RevFeature featureB, RevFeature ancestor,
            RevFeatureType featureType) {

        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(
                (SimpleFeatureType) featureType.type());
        ImmutableList<Optional<Object>> valuesA = featureA.getValues();
        ImmutableList<Optional<Object>> valuesB = featureB.getValues();
        ImmutableList<Optional<Object>> valuesAncestor = ancestor.getValues();
        ImmutableList<PropertyDescriptor> descriptors = featureType.sortedDescriptors();
        for (int i = 0; i < descriptors.size(); i++) {
            PropertyDescriptor descriptor = descriptors.get(i);
            boolean isGeom = Geometry.class.isAssignableFrom(descriptor.getType().getBinding());
            Name name = descriptor.getName();
            Optional<Object> valueAncestor = valuesAncestor.get(i);
            Optional<Object> valueA = valuesA.get(i);
            Optional<Object> valueB = valuesB.get(i);
            if (!valueA.equals(valueAncestor)) {
                Optional<Object> merged = valueA;
                if (isGeom && !valueB.equals(valueAncestor)) { // true merge is only done with
                                                               // geometries
                    GeometryAttributeDiff diffB = new GeometryAttributeDiff(
                            Optional.fromNullable((Geometry) valueAncestor.orNull()),
                            Optional.fromNullable((Geometry) valueB.orNull()));
                    merged = (Optional<Object>) diffB.applyOn(valueA);
                }
                featureBuilder.set(name, merged.orNull());
            } else {
                featureBuilder.set(name, valueB.orNull());
            }
        }
        return featureBuilder.buildFeature(nodeRefA.name());

    }

    public MergeFeaturesOp setFirstFeature(NodeRef feature) {
        this.nodeRefA = feature;
        return this;
    }

    public MergeFeaturesOp setSecondFeature(NodeRef feature) {
        this.nodeRefB = feature;
        return this;
    }

    public MergeFeaturesOp setAncestorFeature(NodeRef ancestor) {
        this.ancestorRef = ancestor;
        return this;
    }

}
TOP

Related Classes of org.locationtech.geogig.api.plumbing.merge.MergeFeaturesOp

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.