/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geogig.geoserver.gwc;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.jdt.annotation.Nullable;
import org.geotools.geometry.jts.JTS;
import org.locationtech.geogig.model.Bounded;
import org.locationtech.geogig.model.Bucket;
import org.locationtech.geogig.model.NodeRef;
import org.locationtech.geogig.plumbing.diff.PreOrderDiffWalk;
import org.locationtech.geogig.plumbing.diff.PreOrderDiffWalk.BucketIndex;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
class MinimalDiffBoundsConsumer implements PreOrderDiffWalk.Consumer {
private static final GeometryFactory GEOM_FACTORY = CompactMultiPoint.GEOM_FACTORY;
/**
* Accumulates punctual differences to save heap
*/
private CompactMultiPoint points = new CompactMultiPoint();
/**
* Accumulates non punctual differences (i.e. bounding polygons)
*/
private List<Geometry> nonPoints = new LinkedList<>();
private final Lock lock = new ReentrantLock();
/**
* @return a single geometry product of unioning all the bounding boxes acquired while
* traversing the diff
*/
public Geometry buildGeometry() {
List<Geometry> geomList = nonPoints;
nonPoints = null;
if (!points.isEmpty()) {
geomList.add(points);
}
points = null;
Geometry buildGeometry = GEOM_FACTORY.buildGeometry(geomList);
geomList.clear();
Geometry union = buildGeometry.union();
return union;
}
@Override
public boolean feature(@Nullable final NodeRef left, @Nullable final NodeRef right) {
addEnv(left);
addEnv(right);
return true;
}
@Override
public boolean tree(@Nullable final NodeRef left, @Nullable final NodeRef right) {
if (left == null) {
addEnv(right);
return false;
} else if (right == null) {
addEnv(left);
return false;
}
return true;
}
@Override
public boolean bucket(final NodeRef leftParent, final NodeRef rightParent,
final BucketIndex bucketIndex, @Nullable final Bucket left,
@Nullable final Bucket right) {
if (left == null) {
addEnv(right);
return false;
} else if (right == null) {
addEnv(left);
return false;
}
return true;
}
private void addEnv(@Nullable Bounded node) {
if (node == null) {
return;
}
final Envelope env = node.bounds().orNull();
if (env == null || env.isNull()) {
return;
}
if (isPoint(env)) {
lock.lock();
try {
points.add(env.getMinX(), env.getMinY());
} finally {
lock.unlock();
}
return;
}
Geometry geom;
if (isOrthoLine(env)) {
// handle the case where the envelope is given by an orthogonal line so we don't add a
// zero area polygon
double width = env.getWidth();
GrowableCoordinateSequence cs = new GrowableCoordinateSequence();
if (width == 0D) {
cs.add(env.getMinX(), env.getMinY());
cs.add(env.getMinX(), env.getMaxY());
} else {
cs.add(env.getMinX(), env.getMinY());
cs.add(env.getMaxX(), env.getMinY());
}
geom = GEOM_FACTORY.createLineString(cs);
} else {
geom = JTS.toGeometry(env, GEOM_FACTORY);
}
lock.lock();
try {
nonPoints.add(geom);
} finally {
lock.unlock();
}
}
private boolean isOrthoLine(Envelope env) {
return env.getArea() == 0D && env.getWidth() > 0D || env.getHeight() > 0D;
}
private boolean isPoint(Envelope env) {
return env.getWidth() == 0D && env.getHeight() == 0D;
}
@Override
public void endTree(@Nullable final NodeRef left, @Nullable final NodeRef right) {
// nothing to do, intentionally blank
}
@Override
public void endBucket(NodeRef leftParent, NodeRef rightParent, final BucketIndex bucketIndex,
@Nullable final Bucket left, @Nullable final Bucket right) {
// nothing to do, intentionally blank
}
}