Package org.locationtech.geogig.api.plumbing

Source Code of org.locationtech.geogig.api.plumbing.DiffBounds$BoundsWalk

/* Copyright (c) 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:
* Jillian Crossley (Cornell University) - initial implementation
*/
package org.locationtech.geogig.api.plumbing;

import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Preconditions.checkArgument;

import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.locationtech.geogig.api.AbstractGeoGigOp;
import org.locationtech.geogig.api.Bounded;
import org.locationtech.geogig.api.Bucket;
import org.locationtech.geogig.api.Node;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.Ref;
import org.locationtech.geogig.api.RevFeatureType;
import org.locationtech.geogig.api.RevTree;
import org.locationtech.geogig.api.plumbing.diff.DiffSummary;
import org.locationtech.geogig.api.plumbing.diff.PreOrderDiffWalk;
import org.locationtech.geogig.api.plumbing.diff.PathFilteringDiffConsumer;
import org.locationtech.geogig.storage.ObjectDatabase;
import org.opengis.feature.type.FeatureType;
import org.opengis.geometry.BoundingBox;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.vividsolutions.jts.geom.Envelope;

/**
* Computes the bounds of the difference between the two trees instead of the actual diffs.
*
*/
public class DiffBounds extends AbstractGeoGigOp<DiffSummary<BoundingBox, BoundingBox>> {

    private String oldVersion;

    private String newVersion;

    private boolean cached;

    private List<String> pathFilters;

    private CoordinateReferenceSystem crs;

    public DiffBounds setOldVersion(String oldVersion) {
        this.oldVersion = oldVersion;
        this.pathFilters = ImmutableList.of();
        return this;
    }

    public DiffBounds setNewVersion(String newVersion) {
        this.newVersion = newVersion;
        return this;
    }

    public DiffBounds setCompareIndex(boolean cached) {
        this.cached = cached;
        return this;
    }

    public DiffBounds setPathFilters(@Nullable final List<String> pathFilters) {
        if (null == pathFilters) {
            this.pathFilters = ImmutableList.of();
        } else {
            this.pathFilters = ImmutableList.copyOf(pathFilters);
        }
        return this;
    }

    /**
     * @param crs the CRS to compute the bounds in. Defaults to {@code EPSG:4326} with long/lat axis
     *        order if not set.
     */
    public DiffBounds setCRS(@Nullable CoordinateReferenceSystem crs) {
        this.crs = crs;
        return this;
    }

    @Override
    protected DiffSummary<BoundingBox, BoundingBox> _call() {
        checkArgument(cached && oldVersion == null || !cached, String.format(
                "compare index allows only one revision to check against, got %s / %s", oldVersion,
                newVersion));

        checkArgument(newVersion == null || oldVersion != null,
                "If new rev spec is specified then old rev spec is mandatory");

        final String leftRefSpec = fromNullable(oldVersion).or(Ref.HEAD);
        final String rightRefSpec = fromNullable(newVersion).or(
                cached ? Ref.STAGE_HEAD : Ref.WORK_HEAD);

        RevTree left = resolveTree(leftRefSpec);
        RevTree right = resolveTree(rightRefSpec);

        ObjectDatabase leftSource = resolveSafeDb(leftRefSpec);
        ObjectDatabase rightSource = resolveSafeDb(rightRefSpec);
        PreOrderDiffWalk visitor = new PreOrderDiffWalk(left, right, leftSource, rightSource);
        CoordinateReferenceSystem crs = resolveCrs();
        BoundsWalk walk = new BoundsWalk(crs, stagingDatabase());
        PreOrderDiffWalk.Consumer consumer = walk;
        if (!pathFilters.isEmpty()) {
            consumer = new PathFilteringDiffConsumer(pathFilters, walk);
        }
        visitor.walk(consumer);
        DiffSummary<BoundingBox, BoundingBox> diffBounds = walk.getResult();
        return diffBounds;
    }

    private CoordinateReferenceSystem resolveCrs() {
        if (this.crs != null) {
            return this.crs;
        }
        CoordinateReferenceSystem defaultCrs;
        try {
            defaultCrs = CRS.decode("EPSG:4326", true);
        } catch (Exception e) {
            throw Throwables.propagate(e);
        }
        return defaultCrs;
    }

    /**
     * If {@code refSpec} can easily be determined to be on the object database (e.g. its a ref),
     * then returns the repository object database, otherwise the staging database, just to be safe
     */
    private ObjectDatabase resolveSafeDb(String refSpec) {
        Optional<Ref> ref = command(RefParse.class).setName(refSpec).call();
        if (ref.isPresent()) {
            ObjectId id = ref.get().getObjectId();
            return objectDatabase().exists(id) ? objectDatabase() : stagingDatabase();
        }
        return stagingDatabase();
    }

    private RevTree resolveTree(String refSpec) {

        Optional<ObjectId> id = command(ResolveTreeish.class).setTreeish(refSpec).call();
        Preconditions.checkState(id.isPresent(), "%s did not resolve to a tree", refSpec);

        return stagingDatabase().getTree(id.get());
    }

    private static class BoundsWalk implements PreOrderDiffWalk.Consumer {

        private DiffSummary<BoundingBox, BoundingBox> result;

        private ReferencedEnvelope leftEnv;

        private ReferencedEnvelope rightEnv;

        private final CoordinateReferenceSystem crs;

        private final ReferencedEnvelope leftHelper, rightHelper;

        private final ObjectDatabase source;

        private final Map<ObjectId, MathTransform> transformsByMetadataId;

        private Optional<ObjectId> currentDefaultLefMetadataId = Optional.absent();

        private Optional<ObjectId> currentDefaultRightMetadataId = Optional.absent();

        public BoundsWalk(CoordinateReferenceSystem crs, ObjectDatabase source) {
            this.crs = crs;
            this.source = source;
            this.transformsByMetadataId = Maps.newHashMap();
            leftEnv = new ReferencedEnvelope(this.crs);
            rightEnv = new ReferencedEnvelope(this.crs);
            leftHelper = new ReferencedEnvelope(this.crs);
            rightHelper = new ReferencedEnvelope(this.crs);
        }

        @Override
        public void feature(@Nullable Node left, @Nullable Node right) {
            setEnv(left, leftHelper, md(left).or(currentDefaultLefMetadataId));
            setEnv(right, rightHelper, md(right).or(currentDefaultRightMetadataId));
            if (!leftHelper.equals(rightHelper)) {
                leftEnv.expandToInclude(leftHelper);
                rightEnv.expandToInclude(rightHelper);
            }
        }

        @Override
        public boolean tree(@Nullable Node left, @Nullable Node right) {
            Optional<ObjectId> leftMd = md(left);
            Optional<ObjectId> rightMd = md(right);
            if (leftMd.isPresent()) {
                currentDefaultLefMetadataId = leftMd;
            }
            if (rightMd.isPresent()) {
                currentDefaultRightMetadataId = rightMd;
            }
            setEnv(left, leftHelper, leftMd.or(leftMd));
            setEnv(right, rightHelper, rightMd.or(rightMd));
            if (leftHelper.isNull() && rightHelper.isNull()) {
                return false;
            }

            if (leftHelper.isNull()) {
                rightEnv.expandToInclude(rightHelper);
                return false;
            } else if (rightHelper.isNull()) {
                leftEnv.expandToInclude(leftHelper);
                return false;
            }
            return true;
        }

        private Optional<ObjectId> md(@Nullable Node node) {
            return null == node ? Optional.<ObjectId> absent() : node.getMetadataId();
        }

        @Override
        public boolean bucket(final int bucketIndex, final int bucketDepth, @Nullable Bucket left,
                @Nullable Bucket right) {
            setEnv(left, leftHelper, currentDefaultLefMetadataId);
            setEnv(right, rightHelper, currentDefaultRightMetadataId);
            if (leftHelper.isNull() && rightHelper.isNull()) {
                return false;
            }

            if (leftHelper.isNull()) {
                rightEnv.expandToInclude(rightHelper);
                return false;
            } else if (rightHelper.isNull()) {
                leftEnv.expandToInclude(leftHelper);
                return false;
            }
            return true;
        }

        private void setEnv(@Nullable Bounded bounded, ReferencedEnvelope env,
                Optional<ObjectId> metadataId) {
            env.setToNull();
            if (bounded == null) {
                return;
            }
            bounded.expand(env);
            if (env.isNull()) {
                return;
            }
            ObjectId mdid;
            if (metadataId.isPresent()) {
                mdid = metadataId.get();
                MathTransform transform = getMathTransform(mdid);
                if (transform.isIdentity()) {
                    return;
                }
                Envelope targetEnvelope = new ReferencedEnvelope(crs);
                try {
                    int densifyPoints = isPoint(env) ? 1 : 5;
                    JTS.transform(env, targetEnvelope, transform, densifyPoints);
                    env.init(targetEnvelope);
                } catch (TransformException e) {
                    throw Throwables.propagate(e);
                }
            }
        }

        private boolean isPoint(Envelope env) {
            return env.getWidth() == 0D && env.getHeight() == 0D;
        }

        private MathTransform getMathTransform(ObjectId mdid) {
            MathTransform transform = this.transformsByMetadataId.get(mdid);
            if (transform == null) {
                RevFeatureType revtype = source.getFeatureType(mdid);
                FeatureType type = revtype.type();
                CoordinateReferenceSystem sourceCrs = type.getCoordinateReferenceSystem();
                CoordinateReferenceSystem targetCrs = this.crs;
                if (sourceCrs == null) {
                    sourceCrs = targetCrs;
                }
                try {
                    boolean lenient = true;
                    transform = CRS.findMathTransform(sourceCrs, targetCrs, lenient);
                } catch (FactoryException e) {
                    throw Throwables.propagate(e);
                }
                this.transformsByMetadataId.put(mdid, transform);
            }
            return transform;
        }

        @Override
        public void endTree(Node left, Node right) {
            String name = left == null ? right.getName() : left.getName();
            if (NodeRef.ROOT.equals(name)) {
                BoundingBox lbounds = new ReferencedEnvelope(this.leftEnv);
                BoundingBox rbounds = new ReferencedEnvelope(this.rightEnv);
                BoundingBox merged;
                if (lbounds.isEmpty()) {
                    merged = rbounds;
                } else if (rbounds.isEmpty()) {
                    merged = lbounds;
                } else {
                    merged = new ReferencedEnvelope(lbounds);
                    merged.include(rbounds);
                }
                this.result = new DiffSummary<BoundingBox, BoundingBox>(lbounds, rbounds, merged);
            }
        }

        @Override
        public void endBucket(int bucketIndex, int bucketDepth, Bucket left, Bucket right) {
            // nothing to do
        }

        public DiffSummary<BoundingBox, BoundingBox> getResult() {
            DiffSummary<BoundingBox, BoundingBox> r = this.result;
            if (r == null) {
                BoundingBox empty = new ReferencedEnvelope(crs);
                r = new DiffSummary<BoundingBox, BoundingBox>(empty, empty, empty);
            }
            return r;
        }

    }
}
TOP

Related Classes of org.locationtech.geogig.api.plumbing.DiffBounds$BoundsWalk

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.