package org.geotools.renderer.lite;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import org.apache.commons.lang3.tuple.Pair;
import org.geotools.process.function.ProcessFunction;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import mil.nga.giat.geowave.adapter.vector.plugin.DistributedRenderProcess;
import mil.nga.giat.geowave.adapter.vector.render.DistributedRenderOptions;
import mil.nga.giat.geowave.adapter.vector.render.DistributedRenderResult;
import mil.nga.giat.geowave.adapter.vector.render.InternalDistributedRenderProcess;
import mil.nga.giat.geowave.adapter.vector.render.DistributedRenderResult.CompositeGroupResult;
import mil.nga.giat.geowave.adapter.vector.render.PersistableComposite;
import mil.nga.giat.geowave.adapter.vector.render.PersistableRenderedImage;
public class DistributedRenderer extends
StreamingRenderer
{
private final DistributedRenderOptions options;
protected DistributedRenderingBlockingQueue renderQueue;
public DistributedRenderer(
final DistributedRenderOptions options ) {
this.options = options;
}
@Override
List<List<LiteFeatureTypeStyle>> classifyByFeatureProduction(
final List<LiteFeatureTypeStyle> lfts ) {
// strip off a distributed rendering render transform because that is
// what is currently being processed
final List<List<LiteFeatureTypeStyle>> retVal = super.classifyByFeatureProduction(lfts);
for (final List<LiteFeatureTypeStyle> featureTypeStyles : retVal) {
final LiteFeatureTypeStyle transformLfts = featureTypeStyles.get(0);
// there doesn't seem to be an easy way to check if its a
// distributed render transform so for now let's just not allow
// other rendering transformations when distributed rendering is
// employed and strip all transformations
if (transformLfts.transformation instanceof ProcessFunction) {
if ((((ProcessFunction) transformLfts.transformation).getName() != null)
&& ((ProcessFunction) transformLfts.transformation).getName().equals(
DistributedRenderProcess.PROCESS_NAME)) {
transformLfts.transformation = null;
}
}
}
return retVal;
}
@Override
public void setRendererHints(
final Map hints ) {
hints.put(
"maxFiltersToSendToDatastore",
options.getMaxFilters());
hints.put(
StreamingRenderer.LINE_WIDTH_OPTIMIZATION_KEY,
options.isOptimizeLineWidth());
super.setRendererHints(hints);
}
@Override
protected BlockingQueue<RenderingRequest> getRequestsQueue() {
renderQueue = new DistributedRenderingBlockingQueue(
10000);
return renderQueue;
}
public DistributedRenderResult getResult(
final BufferedImage parentImage ) {
return renderQueue.getResult(parentImage);
}
public class DistributedRenderingBlockingQueue extends
RenderingBlockingQueue
{
private static final long serialVersionUID = -1014302908773318665L;
private final Map<Graphics2D, List<Pair<BufferedImage, Composite>>> compositeGroupGraphicsToStyleGraphicsMapping = new LinkedHashMap<>();
private final Map<Graphics2D, Composite> compositeGroupGraphicsToCompositeMapping = new HashMap<>();
public DistributedRenderingBlockingQueue(
final int capacity ) {
super(
capacity);
}
@Override
public void put(
final RenderingRequest e )
throws InterruptedException {
// for merge requests just collect the graphics objects and
// associated composites
if (e instanceof MergeLayersRequest) {
final List<LiteFeatureTypeStyle> lftsList = ((MergeLayersRequest) e).lfts;
final List<Pair<BufferedImage, Composite>> styleGraphics = new ArrayList<>();
final Graphics2D parentGraphics = ((MergeLayersRequest) e).graphics;
for (final LiteFeatureTypeStyle lfts : lftsList) {
if ((lfts.graphics instanceof DelayedBackbufferGraphic) && (lfts.graphics != parentGraphics)) {
final DelayedBackbufferGraphic styleGraphic = (DelayedBackbufferGraphic) lfts.graphics;
if (styleGraphic.image != null) {
styleGraphics.add(Pair.of(
styleGraphic.image,
lfts.composite));
continue;
}
}
// if no style graphic was added, add a null value as a
// placeholder in the list
styleGraphics.add(null);
}
compositeGroupGraphicsToStyleGraphicsMapping.put(
parentGraphics,
styleGraphics);
}
else if (e instanceof MargeCompositingGroupRequest) {
compositeGroupGraphicsToCompositeMapping.put(
((MargeCompositingGroupRequest) e).compositingGroup.graphics,
((MargeCompositingGroupRequest) e).compositingGroup.composite);
}
else {
super.put(e);
}
}
public DistributedRenderResult getResult(
final BufferedImage parentImage ) {
final List<CompositeGroupResult> compositeGroups = new ArrayList<>();
for (final Entry<Graphics2D, List<Pair<BufferedImage, Composite>>> e : compositeGroupGraphicsToStyleGraphicsMapping
.entrySet()) {
final Graphics2D compositeGroupGraphic = e.getKey();
final List<Pair<PersistableRenderedImage, PersistableComposite>> orderedStyles = Lists
.transform(
e.getValue(),
new Function<Pair<BufferedImage, Composite>, Pair<PersistableRenderedImage, PersistableComposite>>() {
@Override
public Pair<PersistableRenderedImage, PersistableComposite> apply(
final Pair<BufferedImage, Composite> input ) {
if (input == null) {
return null;
}
return Pair.of(
new PersistableRenderedImage(
input.getKey()),
input.getValue() == null ? null : new PersistableComposite(
input.getValue()));
}
});
if (compositeGroupGraphic instanceof DelayedBackbufferGraphic) {
final Composite compositeGroupComposite = compositeGroupGraphicsToCompositeMapping
.get(compositeGroupGraphic);
// because mergelayers wasn't writing to the composite
// image, their won't be an image to persist
final PersistableComposite persistableCGC = compositeGroupComposite == null ? null
: new PersistableComposite(
compositeGroupComposite);
compositeGroups.add(new CompositeGroupResult(
persistableCGC,
orderedStyles));
}
else {
// it must be the parent image
compositeGroups.add(new CompositeGroupResult(
null,
orderedStyles));
}
}
return new DistributedRenderResult(
new PersistableRenderedImage(
parentImage),
compositeGroups);
}
}
}