/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.kml.sequence;
import java.util.EmptyStackException;
import java.util.List;
import org.geoserver.kml.KmlEncodingContext;
import org.geoserver.kml.decorator.KmlDecoratorFactory.KmlDecorator;
import org.geoserver.kml.utils.ScaleStyleVisitor;
import org.geoserver.kml.utils.SymbolizerCollector;
import org.geoserver.wms.WMSMapContent;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.filter.function.EnvFunction;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.styling.Style;
import org.geotools.styling.Symbolizer;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import de.micromata.opengis.kml.v_2_2_0.Feature;
import de.micromata.opengis.kml.v_2_2_0.Placemark;
/**
* Creates a sequence of Placemark objects mapping the vector contents of a layer
*
* @author Andrea Aime - GeoSolutions
*/
public class FeatureSequenceFactory implements SequenceFactory<Feature> {
static final String OUTPUT_MODE = "kmlOutputMode";
static final String VECTOR_MODE = "vector";
private SimpleFeatureCollection features;
private List<KmlDecorator> callbacks;
private Style simplified;
private KmlEncodingContext context;
public FeatureSequenceFactory(KmlEncodingContext context, FeatureLayer layer) {
this.context = context;
this.features = context.getCurrentFeatureCollection();
WMSMapContent mapContent = context.getMapContent();
// prepare the encoding context
context.setCurrentLayer(layer);
// prepare the callbacks
callbacks = context.getDecoratorsForClass(Placemark.class);
// prepare the style for this layer
simplified = getSimplifiedStyle(mapContent, layer);
}
private Style getSimplifiedStyle(WMSMapContent mc, Layer layer) {
ScaleStyleVisitor visitor = new ScaleStyleVisitor(mc.getScaleDenominator(),
(SimpleFeatureType) layer.getFeatureSource().getSchema());
try {
layer.getStyle().accept(visitor);
return (Style) visitor.getCopy();
} catch (EmptyStackException e) {
return null;
}
}
@Override
public Sequence<Feature> newSequence() {
return new FeatureGenerator(simplified != null ? context.openIterator(features) : null);
}
public class FeatureGenerator implements Sequence<Feature> {
private FeatureIterator fi;
public FeatureGenerator(FeatureIterator fi) {
if(fi != null) {
// until we are scrolling this generator we are in vector mode
EnvFunction.setLocalValue(OUTPUT_MODE, VECTOR_MODE);
this.fi = fi;
}
}
@Override
public Feature next() {
// already reached the end?
if (fi == null) {
return null;
}
while (fi.hasNext()) {
boolean featureRetrieved = false;
try {
// grab the next feature, with a sentinel to tell us whether there was an
// exception
SimpleFeature sf = (SimpleFeature) fi.next();
featureRetrieved = true;
context.setCurrentFeature(sf);
List<Symbolizer> symbolizers = getSymbolizers(simplified, sf);
if (symbolizers.size() == 0) {
// skip layers that have no active symbolizers
continue;
}
context.setCurrentSymbolizers(symbolizers);
// only create the basic placemark here, the rest is delegated to decorators
Placemark pm = new Placemark();
pm.setId(sf.getID());
// call onto the decorators
for (KmlDecorator callback : callbacks) {
pm = (Placemark) callback.decorate(pm, context);
if (pm == null) {
// we have to skip this one
continue;
}
}
return pm;
} finally {
if (!featureRetrieved) {
// an exception has occurred, release the feature iterator
context.closeIterator(fi);
EnvFunction.setLocalValue(OUTPUT_MODE, null);
}
}
}
// did we reach the end just now?
if (!fi.hasNext()) {
// clean up the output mode, the next layer might be encoded as a raster overlay
EnvFunction.setLocalValue(OUTPUT_MODE, null);
context.closeIterator(fi);
}
return null;
}
private List<Symbolizer> getSymbolizers(Style style, SimpleFeature sf) {
SymbolizerCollector collector = new SymbolizerCollector(sf);
style.accept(collector);
return collector.getSymbolizers();
}
}
}