/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.vfny.geoserver.wms.responses.map.htmlimagemap;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.crs.ReprojectFeatureResults;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureType;
import org.geotools.filter.Expression;
import org.geotools.filter.Filter;
import org.geotools.filter.FilterFactory;
import org.geotools.filter.FilterFactoryFinder;
import org.geotools.filter.FilterType;
import org.geotools.filter.GeometryFilter;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.MapLayer;
import org.geotools.referencing.CRS;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Rule;
import org.geotools.styling.Style;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.vfny.geoserver.wms.WMSMapContext;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.logging.Logger;
/**
* Encodes a set of MapLayers in HTMLImageMap format.
*
* @author Mauro Bartolomeoli
*/
public class EncodeHTMLImageMap {
private static final Logger LOGGER = Logger.getLogger("org.vfny.geoserver.responses.wms.map");
private WMSMapContext mapContext;
/** Filter factory for creating filters */
private final static FilterFactory filterFactory = FilterFactoryFinder
.createFilterFactory();
/**
* Current writer.
* The writer is able to encode a single feature.
*/
private HTMLImageMapWriter writer;
private boolean abortProcess;
/**
* Creates a new EncodeHTMLImageMap object.
*
* @param mapContext current wms context
*/
public EncodeHTMLImageMap(WMSMapContext mapContext) {
this.mapContext = mapContext;
}
/**
* Aborts encoding.
*/
public void abort() {
abortProcess = true;
}
/**
* Encodes the current set of layers.
*
* @param out stream to write the produced map to.
*
* @throws IOException if an error occurs in encoding map
*/
public void encode(final OutputStream out) throws IOException {
// initializes the writer
this.writer = new HTMLImageMapWriter(out, mapContext);
abortProcess = false;
long t = System.currentTimeMillis();
try {
// encodes the various layers
writeLayers();
this.writer.flush();
t = System.currentTimeMillis() - t;
LOGGER.info("HTML ImageMap generated in " + t + " ms");
} catch (IOException ioe) {
if (abortProcess) {
LOGGER.fine("HTML ImageMap encoding aborted");
return;
} else {
throw ioe;
}
} catch (AbortedException ex) {
return;
}
}
/**
* Applies Filters from style rules to the given query, to optimize
* DataStore queries.
* Similar to the method in StreamingRenderer.
*
* @param styles
* @param q
*/
private void processRuleForQuery(FeatureTypeStyle[] styles,
DefaultQuery q) {
try {
// first we check to see if there are >
// "getMaxFiltersToSendToDatastore" rules
// if so, then we dont do anything since no matter what there's too
// many to send down.
// next we check for any else rules. If we find any --> dont send
// anything to Datastore
// next we check for rules w/o filters. If we find any --> dont send
// anything to Datastore
//
// otherwise, we're gold and can "or" together all the fiters then
// AND it with the original filter.
// ie. SELECT * FROM ... WHERE (the_geom && BBOX) AND (filter1 OR
// filter2 OR filter3);
final ArrayList filtersToDS = new ArrayList();
final int stylesLength = styles.length;
int styleRulesLength;
FeatureTypeStyle style;
int u = 0;
Rule r;
for (int t = 0; t < stylesLength; t++) // look at each
// featuretypestyle
{
style = styles[t];
Rule[] rules=style.getRules();
styleRulesLength = rules.length;
for (u = 0; u < styleRulesLength; u++) // look at each
// rule in the
// featuretypestyle
{
r = rules[u];
if (r.getFilter() == null)
return; // uh-oh has no filter (want all rows)
if(r.hasElseFilter())
return; // uh-oh has elseRule
filtersToDS.add(r.getFilter());
}
}
Filter ruleFiltersCombined;
Filter newFilter;
// We're GOLD -- OR together all the Rule's Filters
if (filtersToDS.size() == 1) // special case of 1 filter
{
ruleFiltersCombined = (Filter) filtersToDS.get(0);
} else {
// build it up
ruleFiltersCombined = (Filter) filtersToDS.get(0);
final int size = filtersToDS.size();
for (int t = 1; t < size; t++) // NOTE: dont
// redo 1st one
{
newFilter = (Filter) filtersToDS.get(t);
ruleFiltersCombined = filterFactory.createLogicFilter(
ruleFiltersCombined, newFilter, Filter.LOGIC_OR);
}
}
// combine with the geometry filter (preexisting)
ruleFiltersCombined = filterFactory.createLogicFilter(
q.getFilter(), ruleFiltersCombined, Filter.LOGIC_AND);
// set the actual filter
q.setFilter(ruleFiltersCombined);
} catch (Exception e) {
}
}
/**
* Filters the feature type styles of style returning only
* those that apply to featureType
*
* This methods returns feature types for which
* featureTypeStyle.getFeatureTypeName() matches the name
* of the feature type of featureType, or matches the name of
* any parent type of the feature type of featureType. This
* method returns an empty array in the case of which no rules match.
*