Search in sources :

Example 1 with FeatureInfo

use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.

the class ApplyPatchOp method applyPatch.

private void applyPatch(Patch patch) {
    final WorkingTree workTree = workingTree();
    final StagingDatabase indexDb = stagingDatabase();
    if (reverse) {
        patch = patch.reversed();
    }
    List<FeatureInfo> removed = patch.getRemovedFeatures();
    for (FeatureInfo feature : removed) {
        workTree.delete(NodeRef.parentPath(feature.getPath()), NodeRef.nodeFromPath(feature.getPath()));
    }
    List<FeatureInfo> added = patch.getAddedFeatures();
    for (FeatureInfo feature : added) {
        workTree.insert(NodeRef.parentPath(feature.getPath()), feature.getFeature());
    }
    List<FeatureDiff> diffs = patch.getModifiedFeatures();
    for (FeatureDiff diff : diffs) {
        String path = diff.getPath();
        DepthSearch depthSearch = new DepthSearch(indexDb);
        Optional<NodeRef> noderef = depthSearch.find(workTree.getTree(), path);
        RevFeatureType oldRevFeatureType = command(RevObjectParse.class).setObjectId(noderef.get().getMetadataId()).call(RevFeatureType.class).get();
        String refSpec = Ref.WORK_HEAD + ":" + path;
        RevFeature feature = command(RevObjectParse.class).setRefSpec(refSpec).call(RevFeature.class).get();
        RevFeatureType newRevFeatureType = getFeatureType(diff, feature, oldRevFeatureType);
        ImmutableList<Optional<Object>> values = feature.getValues();
        ImmutableList<PropertyDescriptor> oldDescriptors = oldRevFeatureType.sortedDescriptors();
        ImmutableList<PropertyDescriptor> newDescriptors = newRevFeatureType.sortedDescriptors();
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder((SimpleFeatureType) newRevFeatureType.type());
        Map<Name, Optional<?>> attrs = Maps.newHashMap();
        for (int i = 0; i < oldDescriptors.size(); i++) {
            PropertyDescriptor descriptor = oldDescriptors.get(i);
            if (newDescriptors.contains(descriptor)) {
                Optional<Object> value = values.get(i);
                attrs.put(descriptor.getName(), value);
            }
        }
        Set<Entry<PropertyDescriptor, AttributeDiff>> featureDiffs = diff.getDiffs().entrySet();
        for (Iterator<Entry<PropertyDescriptor, AttributeDiff>> iterator = featureDiffs.iterator(); iterator.hasNext(); ) {
            Entry<PropertyDescriptor, AttributeDiff> entry = iterator.next();
            if (!entry.getValue().getType().equals(TYPE.REMOVED)) {
                Optional<?> oldValue = attrs.get(entry.getKey().getName());
                attrs.put(entry.getKey().getName(), entry.getValue().applyOn(oldValue));
            }
        }
        Set<Entry<Name, Optional<?>>> entries = attrs.entrySet();
        for (Iterator<Entry<Name, Optional<?>>> iterator = entries.iterator(); iterator.hasNext(); ) {
            Entry<Name, Optional<?>> entry = iterator.next();
            featureBuilder.set(entry.getKey(), entry.getValue().orNull());
        }
        SimpleFeature featureToInsert = featureBuilder.buildFeature(NodeRef.nodeFromPath(path));
        workTree.insert(NodeRef.parentPath(path), featureToInsert);
    }
    ImmutableList<FeatureTypeDiff> alteredTrees = patch.getAlteredTrees();
    for (FeatureTypeDiff diff : alteredTrees) {
        Optional<RevFeatureType> featureType;
        if (diff.getOldFeatureType().isNull()) {
            featureType = patch.getFeatureTypeFromId(diff.getNewFeatureType());
            workTree.createTypeTree(diff.getPath(), featureType.get().type());
        } else if (diff.getNewFeatureType().isNull()) {
            workTree.delete(diff.getPath());
        } else {
            featureType = patch.getFeatureTypeFromId(diff.getNewFeatureType());
            workTree.updateTypeTree(diff.getPath(), featureType.get().type());
        }
    }
}
Also used : FeatureInfo(org.locationtech.geogig.api.FeatureInfo) Name(org.opengis.feature.type.Name) WorkingTree(org.locationtech.geogig.repository.WorkingTree) FeatureDiff(org.locationtech.geogig.api.plumbing.diff.FeatureDiff) NodeRef(org.locationtech.geogig.api.NodeRef) Entry(java.util.Map.Entry) RevFeature(org.locationtech.geogig.api.RevFeature) AttributeDiff(org.locationtech.geogig.api.plumbing.diff.AttributeDiff) RevFeatureType(org.locationtech.geogig.api.RevFeatureType) StagingDatabase(org.locationtech.geogig.storage.StagingDatabase) Optional(com.google.common.base.Optional) PropertyDescriptor(org.opengis.feature.type.PropertyDescriptor) SimpleFeature(org.opengis.feature.simple.SimpleFeature) DepthSearch(org.locationtech.geogig.repository.DepthSearch) FeatureTypeDiff(org.locationtech.geogig.api.plumbing.diff.FeatureTypeDiff) RevObjectParse(org.locationtech.geogig.api.plumbing.RevObjectParse) SimpleFeatureBuilder(org.geotools.feature.simple.SimpleFeatureBuilder)

Example 2 with FeatureInfo

use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.

the class Patch method addAddedFeature.

/**
     * Adds a feature to the list of newly added ones
     * 
     * @param path the path of the added feature
     * @param feature the feature
     * @param featureType the feature type of the added feature
     */
public void addAddedFeature(String path, Feature feature, RevFeatureType featureType) {
    addedFeatures.add(new FeatureInfo(feature, featureType, path));
    addFeatureType(featureType);
}
Also used : FeatureInfo(org.locationtech.geogig.api.FeatureInfo)

Example 3 with FeatureInfo

use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.

the class Patch method addRemovedFeature.

/**
     * Adds a feature to the list of removed ones
     * 
     * @param path the path of the removed feature
     * @param feature the feature
     * @param featureType the feature type of the removed feature
     */
public void addRemovedFeature(String path, Feature feature, RevFeatureType featureType) {
    removedFeatures.add(new FeatureInfo(feature, featureType, path));
    addFeatureType(featureType);
}
Also used : FeatureInfo(org.locationtech.geogig.api.FeatureInfo)

Example 4 with FeatureInfo

use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.

the class ResponseWriter method writeMerged.

/**
     * Writes the response for a set of merged features while also supplying the geometry.
     * 
     * @param geogig - a CommandLocator to call commands from
     * @param features - a FeatureInfo iterator to build the response from
     * @throws XMLStreamException
     */
public void writeMerged(final Context geogig, Iterator<FeatureInfo> features) throws XMLStreamException {
    Iterator<GeometryChange> changeIterator = Iterators.transform(features, new Function<FeatureInfo, GeometryChange>() {

        @Override
        public GeometryChange apply(FeatureInfo input) {
            GeometryChange change = null;
            RevFeature revFeature = RevFeatureBuilder.build(input.getFeature());
            RevFeatureType featureType = input.getFeatureType();
            Collection<PropertyDescriptor> attribs = featureType.type().getDescriptors();
            String crsCode = null;
            for (PropertyDescriptor attrib : attribs) {
                PropertyType attrType = attrib.getType();
                if (attrType instanceof GeometryType) {
                    GeometryType gt = (GeometryType) attrType;
                    CoordinateReferenceSystem crs = gt.getCoordinateReferenceSystem();
                    if (crs != null) {
                        try {
                            crsCode = CRS.lookupIdentifier(Citations.EPSG, crs, false);
                        } catch (FactoryException e) {
                            crsCode = null;
                        }
                        if (crsCode != null) {
                            crsCode = "EPSG:" + crsCode;
                        }
                    }
                    break;
                }
            }
            FeatureBuilder builder = new FeatureBuilder(featureType);
            GeogigSimpleFeature simpleFeature = (GeogigSimpleFeature) builder.build(revFeature.getId().toString(), revFeature);
            change = new GeometryChange(simpleFeature, ChangeType.MODIFIED, input.getPath(), crsCode);
            return change;
        }
    });
    while (changeIterator.hasNext()) {
        GeometryChange next = changeIterator.next();
        if (next != null) {
            GeogigSimpleFeature feature = next.getFeature();
            out.writeStartElement("Feature");
            writeElement("change", "MERGED");
            writeElement("id", next.getPath());
            List<Object> attributes = feature.getAttributes();
            for (Object attribute : attributes) {
                if (attribute instanceof Geometry) {
                    writeElement("geometry", ((Geometry) attribute).toText());
                    break;
                }
            }
            if (next.getCRS() != null) {
                writeElement("crs", next.getCRS());
            }
            out.writeEndElement();
        }
    }
}
Also used : FeatureBuilder(org.locationtech.geogig.api.FeatureBuilder) RevFeatureBuilder(org.locationtech.geogig.api.RevFeatureBuilder) PropertyDescriptor(org.opengis.feature.type.PropertyDescriptor) FactoryException(org.opengis.referencing.FactoryException) FeatureInfo(org.locationtech.geogig.api.FeatureInfo) PropertyType(org.opengis.feature.type.PropertyType) Geometry(com.vividsolutions.jts.geom.Geometry) GeometryType(org.opengis.feature.type.GeometryType) RevFeature(org.locationtech.geogig.api.RevFeature) Collection(java.util.Collection) RevObject(org.locationtech.geogig.api.RevObject) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) GeogigSimpleFeature(org.locationtech.geogig.api.GeogigSimpleFeature) RevFeatureType(org.locationtech.geogig.api.RevFeatureType)

Example 5 with FeatureInfo

use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.

the class MergeOp method _call.

/**
     * Executes the merge operation.
     * 
     * @return always {@code true}
     */
@Override
protected MergeReport _call() throws RuntimeException {
    Preconditions.checkArgument(commits.size() > 0, "No commits specified for merge.");
    Preconditions.checkArgument(!(ours && theirs), "Cannot use both --ours and --theirs.");
    final Optional<Ref> currHead = command(RefParse.class).setName(Ref.HEAD).call();
    Preconditions.checkState(currHead.isPresent(), "Repository has no HEAD, can't rebase.");
    Ref headRef = currHead.get();
    ObjectId oursId = headRef.getObjectId();
    // Preconditions.checkState(currHead.get() instanceof SymRef,
    // "Can't rebase from detached HEAD");
    // SymRef headRef = (SymRef) currHead.get();
    // final String currentBranch = headRef.getTarget();
    getProgressListener().started();
    boolean fastForward = true;
    boolean changed = false;
    Optional<MergeScenarioReport> mergeScenario = Optional.absent();
    List<CommitAncestorPair> pairs = Lists.newArrayList();
    boolean hasConflictsOrAutomerge;
    List<RevCommit> revCommits = Lists.newArrayList();
    if (!ObjectId.NULL.equals(headRef.getObjectId())) {
        revCommits.add(repository().getCommit(headRef.getObjectId()));
    }
    for (ObjectId commitId : commits) {
        revCommits.add(repository().getCommit(commitId));
    }
    hasConflictsOrAutomerge = command(CheckMergeScenarioOp.class).setCommits(revCommits).call().booleanValue();
    if (hasConflictsOrAutomerge && !theirs) {
        Preconditions.checkState(commits.size() < 2, "Conflicted merge.\nCannot merge more than two commits when conflicts exist" + " or features have been modified in several histories");
        RevCommit headCommit = repository().getCommit(headRef.getObjectId());
        ObjectId commitId = commits.get(0);
        Preconditions.checkArgument(!ObjectId.NULL.equals(commitId), "Cannot merge a NULL commit.");
        Preconditions.checkArgument(repository().commitExists(commitId), "Not a valid commit: " + commitId.toString());
        final RevCommit targetCommit = repository().getCommit(commitId);
        Optional<ObjectId> ancestorCommit = command(FindCommonAncestor.class).setLeft(headCommit).setRight(targetCommit).call();
        pairs.add(new CommitAncestorPair(commitId, ancestorCommit.get()));
        mergeScenario = Optional.of(command(ReportMergeScenarioOp.class).setMergeIntoCommit(headCommit).setToMergeCommit(targetCommit).call());
        List<FeatureInfo> merged = mergeScenario.get().getMerged();
        for (FeatureInfo feature : merged) {
            this.workingTree().insert(NodeRef.parentPath(feature.getPath()), feature.getFeature());
            Iterator<DiffEntry> unstaged = workingTree().getUnstaged(null);
            index().stage(getProgressListener(), unstaged, 0);
            changed = true;
            fastForward = false;
        }
        List<DiffEntry> unconflicting = mergeScenario.get().getUnconflicted();
        if (!unconflicting.isEmpty()) {
            index().stage(getProgressListener(), unconflicting.iterator(), 0);
            changed = true;
            fastForward = false;
        }
        workingTree().updateWorkHead(index().getTree().getId());
        List<Conflict> conflicts = mergeScenario.get().getConflicts();
        if (!ours && !conflicts.isEmpty()) {
            // In case we use the "ours" strategy, we do nothing. We ignore conflicting
            // changes and leave the current elements
            command(UpdateRef.class).setName(Ref.MERGE_HEAD).setNewValue(commitId).call();
            command(UpdateRef.class).setName(Ref.ORIG_HEAD).setNewValue(headCommit.getId()).call();
            command(ConflictsWriteOp.class).setConflicts(conflicts).call();
            StringBuilder msg = new StringBuilder();
            Optional<Ref> ref = command(ResolveBranchId.class).setObjectId(commitId).call();
            if (ref.isPresent()) {
                msg.append("Merge branch " + ref.get().getName());
            } else {
                msg.append("Merge commit '" + commitId.toString() + "'. ");
            }
            msg.append("\n\nConflicts:\n");
            for (Conflict conflict : mergeScenario.get().getConflicts()) {
                msg.append("\t" + conflict.getPath() + "\n");
            }
            command(SaveMergeCommitMessageOp.class).setMessage(msg.toString()).call();
            StringBuilder sb = new StringBuilder();
            for (Conflict conflict : conflicts) {
                sb.append("CONFLICT: Merge conflict in " + conflict.getPath() + "\n");
            }
            sb.append("Automatic merge failed. Fix conflicts and then commit the result.\n");
            throw new MergeConflictsException(sb.toString(), headCommit.getId(), commitId);
        }
    } else {
        Preconditions.checkState(!hasConflictsOrAutomerge || commits.size() < 2, "Conflicted merge.\nCannot merge more than two commits when conflicts exist" + " or features have been modified in several histories");
        for (ObjectId commitId : commits) {
            ProgressListener subProgress = subProgress(100.f / commits.size());
            Preconditions.checkArgument(!ObjectId.NULL.equals(commitId), "Cannot merge a NULL commit.");
            Preconditions.checkArgument(repository().commitExists(commitId), "Not a valid commit: " + commitId.toString());
            subProgress.started();
            if (ObjectId.NULL.equals(headRef.getObjectId())) {
                // Fast-forward
                if (headRef instanceof SymRef) {
                    final String currentBranch = ((SymRef) headRef).getTarget();
                    command(UpdateRef.class).setName(currentBranch).setNewValue(commitId).call();
                    headRef = (SymRef) command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call().get();
                } else {
                    headRef = command(UpdateRef.class).setName(headRef.getName()).setNewValue(commitId).call().get();
                }
                workingTree().updateWorkHead(commitId);
                index().updateStageHead(commitId);
                subProgress.complete();
                changed = true;
                continue;
            }
            RevCommit headCommit = repository().getCommit(headRef.getObjectId());
            final RevCommit targetCommit = repository().getCommit(commitId);
            Optional<ObjectId> ancestorCommit = command(FindCommonAncestor.class).setLeft(headCommit).setRight(targetCommit).call();
            pairs.add(new CommitAncestorPair(commitId, ancestorCommit.get()));
            subProgress.setProgress(10.f);
            Preconditions.checkState(ancestorCommit.isPresent(), "No ancestor commit could be found.");
            if (commits.size() == 1) {
                mergeScenario = Optional.of(command(ReportMergeScenarioOp.class).setMergeIntoCommit(headCommit).setToMergeCommit(targetCommit).call());
                if (ancestorCommit.get().equals(headCommit.getId())) {
                    // Fast-forward
                    if (headRef instanceof SymRef) {
                        final String currentBranch = ((SymRef) headRef).getTarget();
                        command(UpdateRef.class).setName(currentBranch).setNewValue(commitId).call();
                        headRef = (SymRef) command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call().get();
                    } else {
                        headRef = command(UpdateRef.class).setName(headRef.getName()).setNewValue(commitId).call().get();
                    }
                    workingTree().updateWorkHead(commitId);
                    index().updateStageHead(commitId);
                    subProgress.complete();
                    changed = true;
                    continue;
                } else if (ancestorCommit.get().equals(commitId)) {
                    continue;
                }
            }
            // get changes
            Iterator<DiffEntry> diff = command(DiffTree.class).setOldTree(ancestorCommit.get()).setNewTree(targetCommit.getId()).setReportTrees(true).call();
            // stage changes
            index().stage(new SubProgressListener(subProgress, 100.f), diff, 0);
            changed = true;
            fastForward = false;
            workingTree().updateWorkHead(index().getTree().getId());
            subProgress.complete();
        }
    }
    if (!changed) {
        throw new NothingToCommitException("The branch has already been merged.");
    }
    RevCommit mergeCommit = commit(fastForward);
    MergeReport result = new MergeReport(mergeCommit, mergeScenario, oursId, pairs);
    return result;
}
Also used : ReportMergeScenarioOp(org.locationtech.geogig.api.plumbing.merge.ReportMergeScenarioOp) SubProgressListener(org.locationtech.geogig.api.SubProgressListener) FeatureInfo(org.locationtech.geogig.api.FeatureInfo) MergeScenarioReport(org.locationtech.geogig.api.plumbing.merge.MergeScenarioReport) UpdateSymRef(org.locationtech.geogig.api.plumbing.UpdateSymRef) SymRef(org.locationtech.geogig.api.SymRef) RevCommit(org.locationtech.geogig.api.RevCommit) DiffEntry(org.locationtech.geogig.api.plumbing.diff.DiffEntry) ObjectId(org.locationtech.geogig.api.ObjectId) UpdateRef(org.locationtech.geogig.api.plumbing.UpdateRef) UpdateRef(org.locationtech.geogig.api.plumbing.UpdateRef) UpdateSymRef(org.locationtech.geogig.api.plumbing.UpdateSymRef) Ref(org.locationtech.geogig.api.Ref) SymRef(org.locationtech.geogig.api.SymRef) NodeRef(org.locationtech.geogig.api.NodeRef) ProgressListener(org.locationtech.geogig.api.ProgressListener) SubProgressListener(org.locationtech.geogig.api.SubProgressListener) Conflict(org.locationtech.geogig.api.plumbing.merge.Conflict) FindCommonAncestor(org.locationtech.geogig.api.plumbing.FindCommonAncestor)

Aggregations

FeatureInfo (org.locationtech.geogig.api.FeatureInfo)9 RevFeature (org.locationtech.geogig.api.RevFeature)6 RevFeatureType (org.locationtech.geogig.api.RevFeatureType)5 NodeRef (org.locationtech.geogig.api.NodeRef)4 RevObject (org.locationtech.geogig.api.RevObject)4 Entry (java.util.Map.Entry)3 ObjectId (org.locationtech.geogig.api.ObjectId)3 RevObjectParse (org.locationtech.geogig.api.plumbing.RevObjectParse)3 PropertyDescriptor (org.opengis.feature.type.PropertyDescriptor)3 ByteArrayOutputStream (java.io.ByteArrayOutputStream)2 IOException (java.io.IOException)2 RevCommit (org.locationtech.geogig.api.RevCommit)2 FindCommonAncestor (org.locationtech.geogig.api.plumbing.FindCommonAncestor)2 DiffEntry (org.locationtech.geogig.api.plumbing.diff.DiffEntry)2 FeatureDiff (org.locationtech.geogig.api.plumbing.diff.FeatureDiff)2 DepthSearch (org.locationtech.geogig.repository.DepthSearch)2 TextSerializationFactory (org.locationtech.geogig.storage.text.TextSerializationFactory)2 Optional (com.google.common.base.Optional)1 Geometry (com.vividsolutions.jts.geom.Geometry)1 Collection (java.util.Collection)1