Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add new sources of imagemap extension to 2.2.x timeline.

  • Loading branch information...
commit 304d62ef55d0262b751ad2e367424a709d493523 1 parent 742fd2a
@outaTiME authored
Showing with 939 additions and 850 deletions.
  1. +297 −246 src/extension/imagemap/src/main/java/org/vfny/geoserver/wms/responses/map/htmlimagemap/EncodeHTMLImageMap.java
  2. +520 −482 src/extension/imagemap/src/main/java/org/vfny/geoserver/wms/responses/map/htmlimagemap/HTMLImageMapWriter.java
  3. +3 −3 ...ap/src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BasicPolygons.txt
  4. +2 −2 .../src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCenters.txt
  5. +2 −2 ...src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCenters2.txt
  6. +2 −2 ...src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCenters3.txt
  7. +2 −2 ...src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCenters4.txt
  8. +3 −3 ...resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCentersMultiPoint.txt
  9. +3 −3 ...src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/CollectionSample.txt
  10. +1 −1  ...src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/PolygonWithHoles.txt
  11. +1 −1  ...t/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/PolygonWithSkippedHoles.txt
  12. +1 −1  ...src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/ProjectedPolygon.txt
  13. +5 −5 ...map/src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/RoadSegments.txt
  14. +3 −3 ...test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/RoadSegmentsFiltered.txt
  15. +94 −94 .../imagemap/src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/States.txt
View
543 ...tension/imagemap/src/main/java/org/vfny/geoserver/wms/responses/map/htmlimagemap/EncodeHTMLImageMap.java
@@ -7,13 +7,15 @@
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
+import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.wms.WMSMapContent;
import org.geoserver.wms.WebMap;
import org.geotools.data.DataUtilities;
-import org.geotools.data.DefaultQuery;
import org.geotools.data.Query;
import org.geotools.data.crs.ReprojectFeatureResults;
import org.geotools.data.simple.SimpleFeatureCollection;
@@ -21,6 +23,8 @@
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.GeoTools;
import org.geotools.feature.FeatureTypes;
+import org.geotools.filter.IllegalFilterException;
+import org.geotools.filter.visitor.SimplifyingFilterVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.Layer;
import org.geotools.referencing.CRS;
@@ -29,32 +33,37 @@
import org.geotools.styling.Rule;
import org.geotools.styling.Style;
import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.feature.type.AttributeDescriptor;
+import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
-import org.opengis.filter.spatial.BBOX;
-import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.TransformException;
+
+import com.vividsolutions.jts.geom.Envelope;
/**
* Encodes a set of MapLayers in HTMLImageMap format.
*
- * @author Mauro Bartolomeoli
+ * @author Mauro Bartolomeoli
+ * @author Ariel Falduto
*/
public class EncodeHTMLImageMap extends WebMap{
private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.responses.wms.map");
-
+
/** Filter factory for creating filters */
- private final static FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints());
-
- /**
- * Current writer.
+ private final static FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints());
+
+ /**
+ * Current writer.
* The writer is able to encode a single feature.
*/
private HTMLImageMapWriter writer;
-
- private final int maxFilterSize=15;
+
+ private double scaleDenominator;
+
+ private static final int RULES = 0;
+ private static final int ELSE_RULES = 1;
/**
* Creates a new EncodeHTMLImageMap object.
@@ -76,11 +85,21 @@ public EncodeHTMLImageMap(WMSMapContent mapContent) {
public void encode(final OutputStream out) throws IOException {
// initializes the writer
this.writer = new HTMLImageMapWriter(out, mapContent);
-
+
+ // calculate scale denominator
+ scaleDenominator = 1;
+ try {
+ scaleDenominator = RendererUtilities.calculateScale(mapContent.getRenderingArea(),
+ mapContent.getMapWidth(), mapContent.getMapHeight(), null);
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "Error calculating scale denominator", e);
+ }
+ LOGGER.log(Level.FINE, "scale denominator = " + scaleDenominator);
+
long t = System.currentTimeMillis();
try {
- // encodes the different layers
+ // encodes the different layers
writeLayers();
this.writer.flush();
@@ -91,142 +110,7 @@ public void encode(final OutputStream out) throws IOException {
}
}
-
- /**
- * Applies Filters from style rules to the given query, to optimize
- * DataStore queries.
- * Similar to the method in StreamingRenderer.
- *
- * @param styles
- * @param q
- */
- private Filter processRuleForQuery(FeatureTypeStyle[] styles) {
- 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 List<Filter> filtersToDS = new ArrayList<Filter>();
-
- 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 null; // uh-oh has no filter (want all rows)
- if(r.hasElseFilter())
- return null; // uh-oh has elseRule
- filtersToDS.add(r.getFilter());
- }
- }
-
-
- Filter ruleFiltersCombined=null;
- Filter newFilter;
- // We're GOLD -- OR together all the Rule's Filters
- if (filtersToDS.size() == 1) // special case of 1 filter
- {
- ruleFiltersCombined = filtersToDS.get(0);
- // OR all filters if they are under maxFilterSize in number, else, do not filter
- } else if(filtersToDS.size()<maxFilterSize) {
- // build it up
- ruleFiltersCombined = filtersToDS.get(0);
- final int size = filtersToDS.size();
- for (int t = 1; t < size; t++) // NOTE: dont
- // redo 1st one
- {
- newFilter = filtersToDS.get(t);
- ruleFiltersCombined = filterFactory.or(
- ruleFiltersCombined, newFilter);
- }
- }
- return ruleFiltersCombined;
- /*
- // combine with the geometry filter (preexisting)
- ruleFiltersCombined = filterFactory.or(
- q.getFilter(), ruleFiltersCombined);
-
- // set the actual filter
- q.setFilter(ruleFiltersCombined);
- */
- } catch (Exception e) {
- return null;
- }
- }
-
- /**
- * Filters the feature type styles of <code>style</code> returning only
- * those that apply to <code>featureType</code>
- * <p>
- * This methods returns feature types for which
- * <code>featureTypeStyle.getFeatureTypeName()</code> matches the name
- * of the feature type of <code>featureType</code>, or matches the name of
- * any parent type of the feature type of <code>featureType</code>. This
- * method returns an empty array in the case of which no rules match.
- * </p>
- * @param style The style containing the feature type styles.
- * @param featureType The feature type being filtered against.
- *
- */
- protected FeatureTypeStyle[] filterFeatureTypeStyles(Style style, SimpleFeatureType featureType) {
- FeatureTypeStyle[] featureTypeStyles = style.getFeatureTypeStyles();
-
- if ((featureTypeStyles == null) || (featureTypeStyles.length == 0)) {
- return new FeatureTypeStyle[0];
- }
-
- List<FeatureTypeStyle> filtered = new ArrayList<FeatureTypeStyle>(featureTypeStyles.length);
-
- for (int i = 0; i < featureTypeStyles.length; i++) {
- FeatureTypeStyle featureTypeStyle = featureTypeStyles[i];
- String featureTypeName = featureTypeStyle.getFeatureTypeName();
- Rule[] rules=featureTypeStyle.getRules();
- if(rules!=null)
- rules=filterRules(rules);
- //does this style have any rules
- if (rules == null || rules.length == 0 ) {
- continue;
- }
- featureTypeStyle.setRules(rules);
-
- //does this style apply to the feature collection
- if (featureType.getTypeName().equalsIgnoreCase(featureTypeName)
- || FeatureTypes.isDecendedFrom(featureType,null,featureTypeName)) {
- filtered.add(featureTypeStyle);
- }
- }
- return filtered.toArray(new FeatureTypeStyle[filtered.size()]);
- }
-
/**
* Evaluates if the supplied scaleDenominator is congruent with a rule defined scale range.
* @param r current rule
@@ -234,41 +118,11 @@ private Filter processRuleForQuery(FeatureTypeStyle[] styles) {
* @return true if scaleDenominator is in the rule defined range
*/
public static boolean isWithInScale(Rule r,double scaleDenominator) {
- return ((r.getMinScaleDenominator() ) <= scaleDenominator)
- && ((r.getMaxScaleDenominator()) > scaleDenominator);
- }
-
+ return ((r.getMinScaleDenominator() ) <= scaleDenominator)
+ && ((r.getMaxScaleDenominator()) > scaleDenominator);
+ }
+
/**
- * Filter given rules, to consider only the rules compatible
- * with the current scale.
- * @param rules
- * @return
- */
- private Rule[] filterRules(Rule[] rules) {
- List<Rule> result=new ArrayList<Rule>();
- for(int count=0;count<rules.length;count++) {
- Rule rule=rules[count];
- double scaleDenominator;
- try {
- scaleDenominator = RendererUtilities.calculateScale(mapContent.getRenderingArea(), mapContent.getMapWidth(), mapContent.getMapHeight(),90);
-
- //is this rule within scale?
- if (EncodeHTMLImageMap.isWithInScale(rule,scaleDenominator)) {
- result.add(rule);
- }
- } catch (TransformException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (FactoryException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- // TODO Auto-generated method stub
- return result.toArray(new Rule[result.size()]);
- }
-
- /**
* Encodes the current set of layers.
*
* @throws IOException if an error occurs during encoding
@@ -276,73 +130,28 @@ public static boolean isWithInScale(Rule r,double scaleDenominator) {
*
* @task TODO: respect layer filtering given by their Styles
*/
- @SuppressWarnings("unchecked")
- private void writeLayers() throws IOException, AbortedException {
- for(Layer layer:mapContent.layers()){
- SimpleFeatureSource fSource;
- fSource = (SimpleFeatureSource) layer.getFeatureSource();
- SimpleFeatureType schema = fSource.getSchema();
- /*FeatureSource fSource = layer.getFeatureSource();
- FeatureType schema = fSource.getSchema();*/
+ private void writeLayers() throws IOException, AbortedException {
+ for(Layer layer:mapContent.layers()){
+
+ // get the data
+ SimpleFeatureSource featureSource = (SimpleFeatureSource) layer.getFeatureSource();
+ SimpleFeatureCollection features = null;
+ SimpleFeatureType schema = featureSource.getSchema();
try {
- ReferencedEnvelope aoi = mapContent.getRenderingArea();
-
- CoordinateReferenceSystem sourceCrs = schema.getGeometryDescriptor().getCoordinateReferenceSystem();
-
- boolean reproject = (sourceCrs != null)
- && !CRS.equalsIgnoreMetadata(aoi.getCoordinateReferenceSystem(), sourceCrs);
- if (reproject) {
- aoi = aoi.transform(sourceCrs, true);
- }
- // apply filters.
- // 1) bbox filter
- BBOX bboxFilter = filterFactory.bbox(schema.getGeometryDescriptor().getLocalName(),
- aoi.getMinX() , aoi.getMinY(), aoi.getMaxX(), aoi.getMaxY(), null);
- Query q = new Query(schema.getTypeName(), bboxFilter);
-
- String mapId = null;
-
- mapId = schema.getTypeName();
-
- writer.write("<map name=\"" + mapId + "\">\n");
-
- // 2) definition query filter
- Query definitionQuery = layer.getQuery();
- LOGGER.info("Definition Query: "+definitionQuery.toString());
- if (!definitionQuery.equals(Query.ALL)) {
- if (q.equals(Query.ALL)) {
- q = (Query) definitionQuery;
- } else {
- q = (Query) DataUtilities.mixQueries(definitionQuery, q, "HTMLImageMapEncoder");
- }
- }
-
- FeatureTypeStyle[] ftsList=filterFeatureTypeStyles(layer.getStyle(), fSource.getSchema());
- // 3) rule filters
- Filter ruleFilter=processRuleForQuery(ftsList);
- if(ruleFilter!=null) {
- // combine with the geometry filter (preexisting)
- ruleFilter = filterFactory.and(
- q.getFilter(), ruleFilter);
-
- // set the actual filter
- //q.setFilter(ruleFilter);
- q = new DefaultQuery(schema.getTypeName(),ruleFilter);
- //q = (Query) DataUtilities.mixQueries(new Query(schema.getTypeName(),ruleFilter), q, "HTMLImageMapEncoder");
- }
- //ensure reprojection occurs, do not trust query, use the wrapper
- SimpleFeatureCollection fColl = null;//fSource.getFeatures(q);
- //FeatureCollection fColl=null;
- if ( reproject ) {
- fColl=new ReprojectFeatureResults( fSource.getFeatures(q),mapContent.getCoordinateReferenceSystem() );
- } else
- fColl=fSource.getFeatures(q);
-
+ features = loadFeatureCollection(featureSource, layer, mapContent,
+ scaleDenominator);
+
+ LOGGER.finer(new StringBuffer("Encode image map using features: ").append(
+ features.toString()).toString());
+
+ writer.write("<map name=\"" + schema.getTypeName() + "\">\n");
// encodes the current layer, using the defined style
- writer.writeFeatures(fColl, ftsList);
+ writer.writeFeatures(features, filterFeatureTypeStyles(layer.getStyle(), schema));
writer.write("</map>\n");
-
+
+ LOGGER.finer(new StringBuffer("Write of image map done!").toString());
+
} catch (IOException ex) {
throw ex;
} catch (AbortedException ae) {
@@ -357,4 +166,246 @@ private void writeLayers() throws IOException, AbortedException {
}
}
}
+
+ /**
+ * Loads the feature collection based on the current styling and the scale denominator.
+ * If no feature is going to be returned a null feature collection will be returned instead
+ * @param featureSource
+ * @param layer
+ * @param mapContent
+ * @param wms
+ * @param scaleDenominator
+ * @return
+ * @throws Exception
+ */
+ public static SimpleFeatureCollection loadFeatureCollection(
+ SimpleFeatureSource featureSource,
+ Layer layer, WMSMapContent mapContent, double scaleDenominator) throws Exception {
+ SimpleFeatureType schema = featureSource.getSchema();
+
+ Envelope envelope = mapContent.getRenderingArea();
+ ReferencedEnvelope aoi = new ReferencedEnvelope(envelope, mapContent
+ .getCoordinateReferenceSystem());
+ CoordinateReferenceSystem sourceCrs = schema
+ .getCoordinateReferenceSystem();
+
+ boolean reprojectBBox = (sourceCrs != null)
+ && !CRS.equalsIgnoreMetadata(
+ aoi.getCoordinateReferenceSystem(), sourceCrs);
+ if (reprojectBBox) {
+ aoi = aoi.transform(sourceCrs, true);
+ }
+
+ Filter filter = createBBoxFilter(schema, aoi);
+
+ // now build the query using only the attributes and the bounding
+ // box needed
+ Query q = new Query(schema.getTypeName());
+ q.setFilter(filter);
+
+ // now, if a definition query has been established for this layer,
+ // be sure to respect it by combining it with the bounding box one.
+ Query definitionQuery = layer.getQuery();
+
+ if (definitionQuery != Query.ALL) {
+ if (q == Query.ALL) {
+ q = (Query) definitionQuery;
+ } else {
+ q = (Query) DataUtilities.mixQueries(definitionQuery, q,
+ "HTMLImageMapEncoder");
+ }
+ }
+
+ // handle startIndex requested by client query
+ q.setStartIndex(definitionQuery.getStartIndex());
+
+ Filter ruleFilter = summarizeRuleFilters(getLayerRules(featureSource
+ .getSchema(), layer.getStyle()), scaleDenominator);
+ Filter finalFilter = joinFilters(q.getFilter(), ruleFilter);
+ if(finalFilter == Filter.EXCLUDE) {
+ // if we don't have any feature to return
+ return null;
+ }
+ q.setFilter(finalFilter);
+
+ // make sure we output in 4326 since that's what KML mandates
+ /* CoordinateReferenceSystem wgs84;
+ try {
+ wgs84 = CRS.decode("EPSG:4326");
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Cannot decode EPSG:4326, the CRS subsystem must be badly broken...");
+ }
+ if (sourceCrs != null && !CRS.equalsIgnoreMetadata(wgs84, sourceCrs)) {
+ return new ReprojectFeatureResults(featureSource.getFeatures(q), wgs84);
+ }
+ */
+
+ if (reprojectBBox) {
+ return new ReprojectFeatureResults(featureSource.getFeatures(q), mapContent.getCoordinateReferenceSystem());
+ }
+
+ return featureSource.getFeatures(q);
+ }
+
+
+ /**
+ * Creates the bounding box filters (one for each geometric attribute)
+ * needed to query a <code>Layer</code>'s feature source to return
+ * just the features for the target rendering extent
+ *
+ * @param schema
+ * the layer's feature source schema
+ * @param bbox
+ * the expression holding the target rendering bounding box
+ * @return an or'ed list of bbox filters, one for each geometric attribute
+ * in <code>attributes</code>. If there are just one geometric
+ * attribute, just returns its corresponding
+ * <code>GeometryFilter</code>.
+ * @throws IllegalFilterException
+ * if something goes wrong creating the filter
+ */
+ private static Filter createBBoxFilter(SimpleFeatureType schema,
+ Envelope bbox) throws IllegalFilterException {
+ List filters = new ArrayList();
+ for (int j = 0; j < schema.getAttributeCount(); j++) {
+ AttributeDescriptor attType = schema.getDescriptor(j);
+
+ if (attType instanceof GeometryDescriptor) {
+ Filter gfilter = filterFactory.bbox(attType.getLocalName(),
+ bbox.getMinX(), bbox.getMinY(), bbox.getMaxX(), bbox
+ .getMaxY(), null);
+ filters.add(gfilter);
+ }
+ }
+
+ if (filters.size() == 0)
+ return Filter.INCLUDE;
+ else if (filters.size() == 1)
+ return (Filter) filters.get(0);
+ else
+ return filterFactory.or(filters);
+ }
+
+ /**
+ * Summarizes, when possible, the rule filters into one.
+ *
+ * @param rules
+ * @param originalFiter
+ * @param scaleDenominator The actual scale denominator, or a value <= 0 if no scale denominator
+ * checks have to be performed
+ * @return
+ */
+ private static Filter summarizeRuleFilters(List[] rules, double scaleDenominator) {
+ if (rules[RULES].size() == 0 && rules[ELSE_RULES].size() > 0)
+ return Filter.EXCLUDE;
+
+ List filters = new ArrayList();
+ for (Iterator it = rules[RULES].iterator(); it.hasNext();) {
+ Rule rule = (Rule) it.next();
+ if(scaleDenominator <= 0 || isWithInScale(rule, scaleDenominator)) {
+ // if there is a single rule asking for all filters, we have to
+ // return everything that the original filter returned already
+ if (rule.getFilter() == null
+ || Filter.INCLUDE.equals(rule.getFilter()))
+ return Filter.INCLUDE;
+ else
+ filters.add(rule.getFilter());
+ }
+ }
+
+ if(filters.size() > 0) {
+ FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
+ return ff.or(filters);
+ } else {
+ return Filter.EXCLUDE;
+ }
+ }
+
+
+ private static List[] getLayerRules(SimpleFeatureType ftype, Style style) {
+ List[] result = new List[] { new ArrayList(), new ArrayList() };
+
+ final String typeName = ftype.getTypeName();
+
+ FeatureTypeStyle[] featureStyles = filterFeatureTypeStyles(style, ftype);
+ final int length = featureStyles.length;
+ for (int i = 0; i < length; i++) {
+ // getting feature styles
+ FeatureTypeStyle fts = featureStyles[i];
+
+ // get applicable rules at the current scale
+ Rule[] ftsRules = fts.getRules();
+ for (int j = 0; j < ftsRules.length; j++) {
+ // getting rule
+ Rule r = ftsRules[j];
+
+ if (r.hasElseFilter()) {
+ result[ELSE_RULES].add(r);
+ } else {
+ result[RULES].add(r);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Filters the feature type styles of <code>style</code> returning only
+ * those that apply to <code>featureType</code>
+ * <p>
+ * This methods returns feature types for which
+ * <code>featureTypeStyle.getFeatureTypeName()</code> matches the name
+ * of the feature type of <code>featureType</code>, or matches the name of
+ * any parent type of the feature type of <code>featureType</code>. This
+ * method returns an empty array in the case of which no rules match.
+ * </p>
+ * @param style The style containing the feature type styles.
+ * @param featureType The feature type being filtered against.
+ *
+ */
+ public static FeatureTypeStyle[] filterFeatureTypeStyles(Style style,
+ SimpleFeatureType ftype) {
+ List<FeatureTypeStyle> featureTypeStyles = style.featureTypeStyles();
+
+ if (featureTypeStyles == null || featureTypeStyles.isEmpty()) {
+ return new FeatureTypeStyle[0];
+ }
+
+ ArrayList<FeatureTypeStyle> filtered = new ArrayList<FeatureTypeStyle>(featureTypeStyles.size());
+ for(FeatureTypeStyle fts : featureTypeStyles) {
+ String ftName = fts.getFeatureTypeName();
+
+ // yeah, ugly, but exactly the same code as the streaming renderer... we should
+ // really factor out this style massaging in a delegate object (StyleOverlord)
+ if(fts.featureTypeNames().isEmpty() || ((ftype.getName().getLocalPart() != null)
+ && (ftype.getName().getLocalPart().equalsIgnoreCase(ftName) ||
+ FeatureTypes.isDecendedFrom(ftype, null, ftName)))) {
+ filtered.add(fts);
+ }
+ }
+
+ return (FeatureTypeStyle[]) filtered.toArray(new FeatureTypeStyle[filtered.size()]);
+ }
+
+
+ private static Filter joinFilters(Filter... filters) {
+ if(filters == null || filters.length == 0) {
+ return Filter.EXCLUDE;
+ }
+
+ Filter result = null;
+ if(filters.length > 0) {
+ FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
+ result = ff.and(Arrays.asList(filters));
+ } else if(filters.length == 1) {
+ result = filters[0];
+ }
+
+ SimplifyingFilterVisitor visitor = new SimplifyingFilterVisitor();
+ return (Filter) result.accept(visitor, null);
+ }
+
+
}
View
1,002 ...tension/imagemap/src/main/java/org/vfny/geoserver/wms/responses/map/htmlimagemap/HTMLImageMapWriter.java
@@ -4,6 +4,7 @@
*/
package org.vfny.geoserver.wms.responses.map.htmlimagemap;
+import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
@@ -12,6 +13,7 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
+import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -30,6 +32,7 @@
import org.geotools.referencing.operation.DefaultMathTransformFactory;
import org.geotools.referencing.operation.matrix.GeneralMatrix;
import org.geotools.renderer.lite.RendererUtilities;
+import org.geotools.styling.ExternalGraphic;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Graphic;
import org.geotools.styling.LineSymbolizer;
@@ -37,6 +40,7 @@
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.SLD;
+import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.Symbolizer;
import org.geotools.styling.TextSymbolizer;
@@ -47,6 +51,7 @@
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
+import org.opengis.style.GraphicalSymbol;
import org.vfny.geoserver.wms.responses.map.htmlimagemap.holes.HolesRemover;
import com.vividsolutions.jts.geom.Coordinate;
@@ -66,6 +71,7 @@
* Encodes a layer in HTMLImageMap format.
*
* @author Mauro Bartolomeoli
+ * @author Ariel Falduto
*/
public class HTMLImageMapWriter extends OutputStreamWriter {
private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger(HTMLImageMapWriter.class.getPackage().getName());
@@ -74,32 +80,32 @@
/** map of geometry class to writer */
private Map<Class<?>,HTMLImageMapFeatureWriter> writers;
-
+
WMSMapContent mapContent=null;
-
+
/** rect representing screen coordinates space **/
Rectangle mapArea=null;
ReferencedEnvelope mapEnv=null;
Polygon clippingBox=null;
-
-
- /**
- * Transformation from layer (world) coordinates to "screen" coordinates.
+
+
+ /**
+ * Transformation from layer (world) coordinates to "screen" coordinates.
*/
private AffineTransform worldToScreen=null;
-
+
/**
* Creates a new HTMLImageMapWriter object.
*
* @param out stream to encode the layer to
* @param config current wms context
- * @throws ClassCastException
- * @throws UnsupportedEncodingException
+ * @throws ClassCastException
+ * @throws UnsupportedEncodingException
*/
- public HTMLImageMapWriter(OutputStream out, WMSMapContent mapContent) throws UnsupportedEncodingException, ClassCastException {
- super(out, guessCharset(mapContent));
-
- this.mapContent=mapContent;
+ public HTMLImageMapWriter(OutputStream out, WMSMapContent mapContent) throws UnsupportedEncodingException, ClassCastException {
+ super(out, guessCharset(mapContent));
+
+ this.mapContent=mapContent;
mapEnv = mapContent.getRenderingArea();
clippingBox=envToGeometry(mapEnv);
mapArea=new Rectangle(mapContent.getMapWidth(),mapContent.getMapHeight());
@@ -117,19 +123,19 @@ private static String guessCharset(WMSMapContent mapContent) {
}
private Polygon envToGeometry(ReferencedEnvelope env) {
-
- Coordinate[] coordinates=new Coordinate[] {
- new Coordinate(env.getMinX(),env.getMinY()),
- new Coordinate(env.getMaxX(),env.getMinY()),
- new Coordinate(env.getMaxX(),env.getMaxY()),
- new Coordinate(env.getMinX(),env.getMaxY()),
- new Coordinate(env.getMinX(),env.getMinY())
- };
- LinearRing bbox=gFac.createLinearRing(coordinates);
- return gFac.createPolygon(bbox, new LinearRing[] {});
- }
-
- /**
+
+ Coordinate[] coordinates=new Coordinate[] {
+ new Coordinate(env.getMinX(),env.getMinY()),
+ new Coordinate(env.getMaxX(),env.getMinY()),
+ new Coordinate(env.getMaxX(),env.getMaxY()),
+ new Coordinate(env.getMinX(),env.getMaxY()),
+ new Coordinate(env.getMinX(),env.getMinY())
+ };
+ LinearRing bbox=gFac.createLinearRing(coordinates);
+ return gFac.createPolygon(bbox, new LinearRing[] {});
+ }
+
+ /**
* Initializes every type of writer (one for every kind of geometry).
*
*/
@@ -144,19 +150,19 @@ private void initWriters() {
writers.put(MultiPolygon.class, new MultiPolygonWriter());
writers.put(GeometryCollection.class, new GeometryCollectionWriter());
}
-
+
/**
* Encodes a newline
*
* @throws IOException if an error occurs during encoding
-
+
public void newline() throws IOException {
super.write('\n');
}*/
/**
* Encodes a single layer (FeatureCollection) using the supplied style.
- *
+ *
* @param fColl layer to encode
* @param style style to use for encoding
* @throws IOException if an error occurs during encoding
@@ -169,25 +175,25 @@ public void writeFeatures(SimpleFeatureCollection fColl, FeatureTypeStyle[] ftsL
try {
SimpleFeatureType featureType = fColl.getSchema();
Class<?> gtype = featureType.getGeometryDescriptor().getType().getBinding();
-
+
// iterates through the single features
iter=fColl.features();
while (iter.hasNext()) {
- ft = iter.next();
+ ft = iter.next();
Geometry geo=(Geometry)ft.getDefaultGeometry();
-
+
if(!clippingBox.contains(geo)) {
- try {
- Geometry clippedGeometry=clippingBox.intersection(geo);
- ft.setDefaultGeometry(clippedGeometry);
- } catch (Throwable e) {
- // ignore and use the original geo
- }
+ try {
+ Geometry clippedGeometry=clippingBox.intersection(geo);
+ ft.setDefaultGeometry(clippedGeometry);
+ } catch (Throwable e) {
+ // ignore and use the original geo
+ }
}
// retrieves the right feature writer (based on the geometry type of the feature)
HTMLImageMapFeatureWriter featureWriter = (HTMLImageMapFeatureWriter) writers.get(ft.getDefaultGeometry().getClass());
// encodes a single feature, using the supplied style and the current featureWriter
- featureWriter.writeFeature(ft,ftsList);
+ featureWriter.writeFeature(ft,ftsList);
ft = null;
}
@@ -195,15 +201,15 @@ public void writeFeatures(SimpleFeatureCollection fColl, FeatureTypeStyle[] ftsL
} catch (NoSuchElementException ex) {
throw new DataSourceException(ex.getMessage(), ex);
} finally {
- if(iter!=null)
+ if(iter!=null)
//make sure we always close
fColl.close(iter);
}
}
-
-
-
+
+
+
/**
* Filters the rules of <code>featureTypeStyle</code> returnting only
* those that apply to <code>feature</code>.
@@ -244,23 +250,23 @@ public void writeFeatures(SimpleFeatureCollection fColl, FeatureTypeStyle[] ftsL
continue;
}
- /*
+ /*
double scaleDenominator;
- try {
- scaleDenominator = RendererUtilities.calculateScale(mapContent.getAreaOfInterest(), mapContent.getMapWidth(), mapContent.getMapHeight(),100);
-
- //is this rule within scale?
- if ( !EncodeHTMLImageMap.isWithInScale(rule,scaleDenominator)) {
- continue;
- }
+ try {
+ scaleDenominator = RendererUtilities.calculateScale(mapContent.getAreaOfInterest(), mapContent.getMapWidth(), mapContent.getMapHeight(),100);
+
+ //is this rule within scale?
+ if ( !EncodeHTMLImageMap.isWithInScale(rule,scaleDenominator)) {
+ continue;
+ }
} catch (TransformException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (FactoryException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }*/
-
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (FactoryException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }*/
+
//does this rule have a filter which applies to the feature
Filter filter = rule.getFilter();
@@ -286,7 +292,7 @@ public void writeFeatures(SimpleFeatureCollection fColl, FeatureTypeStyle[] ftsL
return (Rule[]) filtered.toArray(new Rule[filtered.size()]);
}
-
+
/**
* Base Class for all the feature writers.
@@ -295,129 +301,129 @@ public void writeFeatures(SimpleFeatureCollection fColl, FeatureTypeStyle[] ftsL
* @author Mauro Bartolomeoli
*/
private abstract class HTMLImageMapFeatureWriter {
-
- // stores a series of attributes to append to the feature tag definition
- Map<String,String> extraAttributes=new HashMap<String,String>();
-
- StringBuffer buffer=new StringBuffer();
-
- /**
- * Encodes a single feature.
- * Default implementation.
- * The encoding is accomplished through many phases:
- * 1) reset writer state
- * 2) process supplied style and apply filters to decide if the feature has to be included
- * in output. If the feature has to be included, proceed with the following phases, else go to the
- * next feature.
- * 3) start feature encoding
- * 4) pre geometry encoding
- * 5) actual geometry encoding
- * 6) post geometry encoding
- * 7) end feature encoding
- * @param ft feature to encode
- * @param style style to use for the encoding
- * @param fts "cached" ftss matching the FeatureType of the feature
- * @throws IOException if an error occurs during encoding
- */
- protected void writeFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
- // a new feature begins, reset accumulated info, such as extraAttributes
- reset(ft);
- // process the supplied style and store rendering info for the following phases
- // the style processing applies filters to the feature to decide if it has to be included
- // in output
- if(processStyle(ft,fts)) {
- try {
- Geometry geo=(Geometry)ft.getDefaultGeometry();
- if(geo!=null) {
- // encodes starting element
- startElement(ft,"");
-
- // pre geometry encoding phase
- startGeometry(geo);
- // actual geometry encoding phase
- writeGeometry(geo,buffer);
- // post geometry encoding phase
- endGeometry(geo);
- // encodes ending element
- endElement(ft);
- // if everything has been correctly encoded,
- // we commit the buffer content to the stream
- commitBuffer();
- } else {
- buffer=new StringBuffer();
- if(LOGGER.isLoggable(Level.WARNING))
- LOGGER.warning("null geometry");
- }
- } catch(IOException e) {
- buffer=new StringBuffer();
- if(LOGGER.isLoggable(Level.WARNING))
- LOGGER.warning("Problems encoding shape: "+e.getMessage());
- } catch(Throwable t) {
- buffer=new StringBuffer();
- if(LOGGER.isLoggable(Level.SEVERE))
- LOGGER.severe("Problems encoding shape: "+t.getMessage());
- }
- }
-
- }
-
- protected void commitBuffer() throws IOException {
- write(buffer.toString());
- buffer=new StringBuffer();
- }
-
- /**
- * Encodes a "MultiFeature", a feature with multiple geometries.
- * Default implementation.
- * The encoding is accomplished through many phases:
- * 1) reset writer state
- * 2) process supplied style and apply filters to decide if the feature has to be included
- * in output. If the feature has to be included, proceed with the following phases, else go to the
- * next feature.
- * 3) loops for all the geometries, with the following phases for every single geometry.
- * a) start feature encoding
- * b) pre geometry encoding
- * c) actual geometry encoding
- * d) post geometry encoding
- * e) end feature encoding
- * @param ft feature to encode
- * @param style style to use for the encoding
- * @param fts "cached" ftss matching the FeatureType of the feature
- * @throws IOException if an error occurs during encoding
- */
- protected void writeMultiFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
- reset(ft);
- if(processStyle(ft,fts)) {
- GeometryCollection geomCollection = (GeometryCollection) ft.getDefaultGeometry();
- for (int i = 0; i < geomCollection.getNumGeometries(); i++) {
- try {
- Geometry geo=geomCollection.getGeometryN(i);
- if(geo!=null) {
- startElement(ft,"."+i);
-
- startGeometry(geo);
- writeGeometry(geo,buffer);
- endGeometry(geo);
- endElement(ft);
- commitBuffer();
- } else {
- if(LOGGER.isLoggable(Level.WARNING))
- LOGGER.warning("Problems encoding shape: null geometry");
- }
-
- } catch(IOException e) {
- buffer=new StringBuffer();
- if(LOGGER.isLoggable(Level.WARNING))
- LOGGER.warning("Problems encoding shape: "+e.getMessage());
- } catch(Throwable t) {
- buffer=new StringBuffer();
- if(LOGGER.isLoggable(Level.SEVERE))
- LOGGER.severe("Problems encoding shape: "+t.getMessage());
- }
- }
- }
- }
-
+
+ // stores a series of attributes to append to the feature tag definition
+ Map<String,String> extraAttributes=new HashMap<String,String>();
+
+ StringBuffer buffer=new StringBuffer();
+
+ /**
+ * Encodes a single feature.
+ * Default implementation.
+ * The encoding is accomplished through many phases:
+ * 1) reset writer state
+ * 2) process supplied style and apply filters to decide if the feature has to be included
+ * in output. If the feature has to be included, proceed with the following phases, else go to the
+ * next feature.
+ * 3) start feature encoding
+ * 4) pre geometry encoding
+ * 5) actual geometry encoding
+ * 6) post geometry encoding
+ * 7) end feature encoding
+ * @param ft feature to encode
+ * @param style style to use for the encoding
+ * @param fts "cached" ftss matching the FeatureType of the feature
+ * @throws IOException if an error occurs during encoding
+ */
+ protected void writeFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
+ // a new feature begins, reset accumulated info, such as extraAttributes
+ reset(ft);
+ // process the supplied style and store rendering info for the following phases
+ // the style processing applies filters to the feature to decide if it has to be included
+ // in output
+ if(processStyle(ft,fts)) {
+ try {
+ Geometry geo=(Geometry)ft.getDefaultGeometry();
+ if(geo!=null) {
+ // encodes starting element
+ startElement(ft,"");
+
+ // pre geometry encoding phase
+ startGeometry(geo);
+ // actual geometry encoding phase
+ writeGeometry(geo,buffer);
+ // post geometry encoding phase
+ endGeometry(geo);
+ // encodes ending element
+ endElement(ft);
+ // if everything has been correctly encoded,
+ // we commit the buffer content to the stream
+ commitBuffer();
+ } else {
+ buffer=new StringBuffer();
+ if(LOGGER.isLoggable(Level.WARNING))
+ LOGGER.warning("null geometry");
+ }
+ } catch(IOException e) {
+ buffer=new StringBuffer();
+ if(LOGGER.isLoggable(Level.WARNING))
+ LOGGER.warning("Problems encoding shape: "+e.getMessage());
+ } catch(Throwable t) {
+ buffer=new StringBuffer();
+ if(LOGGER.isLoggable(Level.SEVERE))
+ LOGGER.severe("Problems encoding shape: "+t.getMessage());
+ }
+ }
+
+ }
+
+ protected void commitBuffer() throws IOException {
+ write(buffer.toString());
+ buffer=new StringBuffer();
+ }
+
+ /**
+ * Encodes a "MultiFeature", a feature with multiple geometries.
+ * Default implementation.
+ * The encoding is accomplished through many phases:
+ * 1) reset writer state
+ * 2) process supplied style and apply filters to decide if the feature has to be included
+ * in output. If the feature has to be included, proceed with the following phases, else go to the
+ * next feature.
+ * 3) loops for all the geometries, with the following phases for every single geometry.
+ * a) start feature encoding
+ * b) pre geometry encoding
+ * c) actual geometry encoding
+ * d) post geometry encoding
+ * e) end feature encoding
+ * @param ft feature to encode
+ * @param style style to use for the encoding
+ * @param fts "cached" ftss matching the FeatureType of the feature
+ * @throws IOException if an error occurs during encoding
+ */
+ protected void writeMultiFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
+ reset(ft);
+ if(processStyle(ft,fts)) {
+ GeometryCollection geomCollection = (GeometryCollection) ft.getDefaultGeometry();
+ for (int i = 0; i < geomCollection.getNumGeometries(); i++) {
+ try {
+ Geometry geo=geomCollection.getGeometryN(i);
+ if(geo!=null) {
+ startElement(ft,"."+i);
+
+ startGeometry(geo);
+ writeGeometry(geo,buffer);
+ endGeometry(geo);
+ endElement(ft);
+ commitBuffer();
+ } else {
+ if(LOGGER.isLoggable(Level.WARNING))
+ LOGGER.warning("Problems encoding shape: null geometry");
+ }
+
+ } catch(IOException e) {
+ buffer=new StringBuffer();
+ if(LOGGER.isLoggable(Level.WARNING))
+ LOGGER.warning("Problems encoding shape: "+e.getMessage());
+ } catch(Throwable t) {
+ buffer=new StringBuffer();
+ if(LOGGER.isLoggable(Level.SEVERE))
+ LOGGER.severe("Problems encoding shape: "+t.getMessage());
+ }
+ }
+ }
+ }
+
/**
* Encodes the feature starting tag (area).
*
@@ -429,9 +435,9 @@ protected void writeMultiFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws
*/
protected void startElement(SimpleFeature feature,String suffix)
throws IOException {
- // each feature (multi geometry ones are an exception) is represented by an <area> tag
- // each area tag has an id, equal to the feature id, and a shape (rect, poly or circle)
- writeToBuffer("<area shape=\""+getShape()+"\" id=\""+feature.getID()+suffix+"\" ",buffer);
+ // each feature (multi geometry ones are an exception) is represented by an <area> tag
+ // each area tag has an id, equal to the feature id, and a shape (rect, poly or circle)
+ writeToBuffer("<area shape=\""+getShape()+"\" id=\""+feature.getID()+suffix+"\" ",buffer);
}
/**
@@ -442,17 +448,17 @@ protected void startElement(SimpleFeature feature,String suffix)
* @throws IOException if an error occures during encoding
*/
protected void startGeometry(Geometry geom) throws IOException {
- writeToBuffer(" coords=\"",buffer);
+ writeToBuffer(" coords=\"",buffer);
}
/**
- * Each area tag has a shape attribute, defining the coords attribute meaning. The W3C HTML 4.0
- * standard defines 3 possible shape values: rect, poly and circle.
+ * Each area tag has a shape attribute, defining the coords attribute meaning. The W3C HTML 4.0
+ * standard defines 3 possible shape values: rect, poly and circle.
* @return the writer associated shape value
* @throws IOException if an error occures during encoding
*/
protected abstract String getShape() throws IOException;
-
+
/**
* Encodes the actual geometry.
*
@@ -471,7 +477,7 @@ protected abstract void writeGeometry(Geometry geom,StringBuffer buffer)
* @throws IOException if an error occures during encoding
*/
protected void endGeometry(Geometry geom) throws IOException {
- writeToBuffer("\"",buffer);
+ writeToBuffer("\"",buffer);
}
/**
@@ -482,27 +488,27 @@ protected void endGeometry(Geometry geom) throws IOException {
* @throws IOException if an error occures during encoding
*/
protected void endElement(SimpleFeature feature) throws IOException {
- Iterator<String> iter=extraAttributes.keySet().iterator();
- while(iter.hasNext()) {
- String attrName=iter.next();
- writeToBuffer(" "+attrName+"=\""+extraAttributes.get(attrName)+"\"",buffer);
- }
- writeToBuffer("/>\n",buffer);
+ Iterator<String> iter=extraAttributes.keySet().iterator();
+ while(iter.hasNext()) {
+ String attrName=iter.next();
+ writeToBuffer(" "+attrName+"=\""+extraAttributes.get(attrName)+"\"",buffer);
+ }
+ writeToBuffer("/>\n",buffer);
}
-
+
/**
* Resets writer status.
* extraAttributes is emptied
- * @param ft current feature to encode
+ * @param ft current feature to encode
*/
protected void reset(SimpleFeature ft) {
- extraAttributes=new HashMap<String,String>();
- buffer=new StringBuffer();
+ extraAttributes=new HashMap<String,String>();
+ buffer=new StringBuffer();
}
/**
* Analyze the supplied style and process any matching rule.
- *
+ *
* @param ft feature to which the style is going to be applied
* @param style style to process
* @param ftsList cached fts matching the feature
@@ -510,43 +516,43 @@ protected void reset(SimpleFeature ft) {
* style filters.
* @throws IOException if an error occurs during the process
*/
- protected boolean processStyle(SimpleFeature ft,FeatureTypeStyle[] ftsList)
+ protected boolean processStyle(SimpleFeature ft,FeatureTypeStyle[] ftsList)
throws IOException {
- int total=0;
- for(int i=0;i<ftsList.length;i++) {
- FeatureTypeStyle fts=ftsList[i];
- Rule[] rules=filterRules(fts, ft);
- total+=rules.length;
-
- for(int j=0;j<rules.length;j++)
- processRule(ft,rules[j]);
-
-
- }
- if(total==0)
- return false;
- return true;
- }
-
+ int total=0;
+ for(int i=0;i<ftsList.length;i++) {
+ FeatureTypeStyle fts=ftsList[i];
+ Rule[] rules=filterRules(fts, ft);
+ total+=rules.length;
+
+ for(int j=0;j<rules.length;j++)
+ processRule(ft,rules[j]);
+
+
+ }
+ if(total==0)
+ return false;
+ return true;
+ }
+
/**
* Process a single style rule and apply meaningful style to the feature
* @param ft feature to which the rule has to be applied
* @param rule rule to process
* @throws IOException if an error occurs during the process
- */
- protected void processRule(SimpleFeature ft,Rule rule)
+ */
+ protected void processRule(SimpleFeature ft,Rule rule)
throws IOException {
-
- Symbolizer[] symbolizers=rule.getSymbolizers();
- for(int i=0;i<symbolizers.length;i++) {
- Symbolizer symbolizer=symbolizers[i];
- // process any given symbolizer
- processSymbolizer(ft,rule,symbolizer);
- }
+
+ Symbolizer[] symbolizers=rule.getSymbolizers();
+ for(int i=0;i<symbolizers.length;i++) {
+ Symbolizer symbolizer=symbolizers[i];
+ // process any given symbolizer
+ processSymbolizer(ft,rule,symbolizer);
+ }
}
/**
* Process a single style symbolizer and apply meaningful style to the feature.
- * The default implementation processes TextSymbolizer, using Label definition to apply textual
+ * The default implementation processes TextSymbolizer, using Label definition to apply textual
* attributes to the area tag.
* @param ft feature to which the symbolizer has to be applied
* @param rule current rule to analyze
@@ -554,10 +560,10 @@ protected void processRule(SimpleFeature ft,Rule rule)
* @throws IOException if an error occurs during the process
*/
protected void processSymbolizer(SimpleFeature ft,Rule rule,Symbolizer symbolizer) throws IOException{
- if(symbolizer instanceof TextSymbolizer) {
- // TODO: any check for label definition needed here?
- Expression e = SLD.textLabel((TextSymbolizer) symbolizer);
- // eval label actual value
+ if(symbolizer instanceof TextSymbolizer) {
+ // TODO: any check for label definition needed here?
+ Expression e = SLD.textLabel((TextSymbolizer) symbolizer);
+ // eval label actual value
Object object = e.evaluate(ft);
String value = null;
@@ -572,24 +578,24 @@ protected void processSymbolizer(SimpleFeature ft,Rule rule,Symbolizer symbolize
// the attribute name is equal to the current rule name (if defined, defaults to "title")
// the value of the attribute is the current label value
if ((value != null) && !"".equals(value.trim())) {
- String attrName=rule.getName();
- if(attrName==null || attrName.trim().equals(""))
- attrName="title";
- extraAttributes.put(attrName,value);
+ String attrName=rule.getName();
+ if(attrName==null || attrName.trim().equals(""))
+ attrName="title";
+ extraAttributes.put(attrName,value);
}
- }
+ }
}
-
+
/**
* Gets a point in screen coordinates from a Coordinate in world coordinates
* @param c projected coordinate
* @return screen coordinates in textual form (x,y)
*/
protected String getPoint(Coordinate c) {
- Point2D transformed=worldToScreen.transform(new Point2D.Double(c.x,c.y),null);
- return (int)Math.round(transformed.getX())+","+(int)Math.round(transformed.getY());
+ Point2D transformed=worldToScreen.transform(new Point2D.Double(c.x,c.y),null);
+ return (int)Math.round(transformed.getX())+","+(int)Math.round(transformed.getY());
}
-
+
/**
* Encodes a list of coordinates in textual form (for the coords attribute)
* <i>coords</i> Area attribute<p>
@@ -600,130 +606,162 @@ protected String getPoint(Coordinate c) {
*/
protected void writePathContent(Coordinate[] coords,StringBuffer buf)
throws IOException {
- StringBuffer tempBuf=new StringBuffer();
- int nCoords = coords.length;
+ StringBuffer tempBuf=new StringBuffer();
+ int nCoords = coords.length;
for(int i=0;i<nCoords;i++) {
- Coordinate curr = coords[i];
- String p=getPoint(curr);
-
-
- tempBuf.append(" "+p);
+ Coordinate curr = coords[i];
+ String p=getPoint(curr);
+ tempBuf.append(p);
+ if (i < nCoords - 1) {
+ tempBuf.append(",");
+ }
}
// Close the path if it's not already closed
if(!coords[nCoords-1].equals2D(coords[0]))
- tempBuf.append(" "+coords[0].x+","+coords[0].y);
+ tempBuf.append(","+coords[0].x+","+coords[0].y);
if(tempBuf.length()>0)
- writeToBuffer(tempBuf.substring(1),buf);
+ writeToBuffer(tempBuf.toString(),buf);
else
- throw new IOException("No coordinates");
+ throw new IOException("No coordinates");
}
-
+
protected void writeToBuffer(String substring,StringBuffer buf) {
- buf.append(substring);
- }
+ buf.append(substring);
+ }
- /**
- * Simplifies a geometry to exclude duplicated points. When translating from world
+ /**
+ * Simplifies a geometry to exclude duplicated points. When translating from world
* to screen coordinates it's possible that many world points collapse to a single screen point.
- * Those colliding points are simplified to a single point.
+ * Those colliding points are simplified to a single point.
* @param geom
* @return
*/
Geometry decimate(Geometry geom) {
- DefaultMathTransformFactory f= new DefaultMathTransformFactory();
+ DefaultMathTransformFactory f= new DefaultMathTransformFactory();
MathTransform xform=null;
- try {
- xform = f.createAffineTransform(new GeneralMatrix(worldToScreen.createInverse()));
- Decimator decimator=new Decimator(xform,mapArea);
- geom=decimator.decimate(geom);
- } catch (FactoryException e1) {
-
- } catch (NoninvertibleTransformException e1) {
-
- }
- catch (Exception e1) {
-
- }
- return geom;
-
-
+ try {
+ xform = f.createAffineTransform(new GeneralMatrix(worldToScreen.createInverse()));
+ Decimator decimator=new Decimator(xform,mapArea);
+ geom=decimator.decimate(geom);
+ } catch (FactoryException e1) {
+
+ } catch (NoninvertibleTransformException e1) {
+
+ }
+ catch (Exception e1) {
+
+ }
+ return geom;
+
+
}
}
/**
* FeatureWriter for point geometry features.
- * Currently supports circle WellKnownName Marks.
+ * Currently supports circle WellKnownName Marks.
*/
private class PointWriter extends HTMLImageMapFeatureWriter {
-
- // encodes as a circle shape?
- boolean asCircle=true;
- // encodes as a different shape? (currently not supported--> empty rendering)
- String symbol=null;
- // radius of the circle
- double size=2;
-
- /**
+
+ // encodes as a circle shape?
+ boolean asCircle=true;
+ // encodes as a different shape? (currently not supported--> empty rendering)
+ String symbol=null;
+ // radius of the circle
+ double size=2;
+
+ /**
* Creates a new PointWriter object.
*/
public PointWriter() {
}
-
+
/**
* The shape for points is a circle.
*/
protected String getShape() throws IOException {
- return "circle";
+ return "circle";
}
-
+
/**
* Uses the supplied style to define point rendering.
* Currently it gets WellKnownName from a Mark (circle is the only value correctly rendered by now).
* It also uses the Size parameter to define circle radius.
*/
-
+
protected void processSymbolizer(SimpleFeature ft, Rule rule,Symbolizer symbolizer) throws IOException{
- super.processSymbolizer(ft, rule,symbolizer);
- if(symbolizer instanceof PointSymbolizer) {
- Mark mark=SLD.mark((PointSymbolizer)symbolizer);
- Graphic graphic=SLD.graphic((PointSymbolizer)symbolizer);
- if(graphic!=null && mark!=null) {
- Object oSize=graphic.getSize().evaluate(null);
- if(oSize!=null)
- size=Double.parseDouble(oSize.toString());
- asCircle=SLD.wellKnownName(mark).toLowerCase().equals("circle");
- if(!asCircle)
- symbol=SLD.wellKnownName(mark).toLowerCase();
- }
-
- }
- }
-
-
-
+ super.processSymbolizer(ft, rule,symbolizer);
+ if(symbolizer instanceof PointSymbolizer) {
+
+ Graphic graphic = ((PointSymbolizer)symbolizer).getGraphic();
+
+ if (graphic != null) {
+
+ for (GraphicalSymbol gs : graphic.graphicalSymbols()) {
+ if (gs != null && gs instanceof Mark) {
+ Mark mark = (Mark) gs;
+ LOGGER.fine("PointWriter, wellKnownName: " + SLD.wellKnownName(mark));
+ asCircle = SLD.wellKnownName(mark).toLowerCase().equals("circle");
+ if(!asCircle) {
+ symbol = SLD.wellKnownName(mark).toLowerCase();
+ }
+ }
+
+ }
+
+ Object oSize = graphic.getSize().evaluate(null);
+ if (oSize != null) {
+ size = Double.parseDouble(oSize.toString());
+ }
+
+ if ((graphic.getExternalGraphics() != null) && (graphic.getExternalGraphics().length > 0)) {
+ // external
+ size = 16; // fixed size for all external graphics
+ /* ExternalGraphic graphic_ext = graphic.getExternalGraphics()[0];
+ URL graphicLocation = graphic_ext.getLocation();
+ LOGGER.finer("PointWriter, usign external graph: " + graphicLocation.toString()); */
+
+ LOGGER.finer("PointWriter, usign external graph...");
+
+ }
+
+ // increment size
+ size += 2;
+
+ LOGGER.fine("PointWriter, size: " + size);
+
+ /* } else {
+ LOGGER.fine("PointWriter, no graphic found :("); */
+ }
+
+ }
+ }
+
+
+
/**
* Actually encodes the point.
*
- * @param geom point to encode
+ * @param geom point to encode
*
* @throws IOException if an error occures during encoding
*/
protected void writeGeometry(Geometry geom,StringBuffer buf) throws IOException {
- if(geom instanceof Point) {
- Point p = (Point) geom;
- if(p.getCoordinate()!=null) {
- if(asCircle) {
- writeToBuffer(getPoint(p.getCoordinate())+","+(int)Math.round(size),buf);
- } else{
- throw new IOException("Nothing to encode");
- //TODO: manage different shapes
- }
- } else
- throw new IOException("null point coordinate");
- } else
- throw new IOException("Wrong geometry: it should be a Point");
+ if(geom instanceof Point) {
+ Point p = (Point) geom;
+ if(p.getCoordinate()!=null) {
+ if(asCircle) {
+ writeToBuffer(getPoint(p.getCoordinate())+","+(int)Math.round(size),buf);
+ } else{
+ throw new IOException("Nothing to encode");
+ //TODO: manage different shapes
+ }
+ } else
+ throw new IOException("null point coordinate");
+ } else
+ throw new IOException("Wrong geometry: it should be a Point");
}
}
@@ -736,17 +774,17 @@ protected void writeGeometry(Geometry geom,StringBuffer buf) throws IOException
*/
public MultiPointWriter() {
}
-
+
/**
* Uses writeMultiFeature.
*/
protected void writeFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
- writeMultiFeature(ft, fts);
+ writeMultiFeature(ft, fts);
}
-
+
}
-
+
/**
* FeatureWriter for LineString geometry features.
* A buffer is applied to the linear geometry to transform it to a Polygon.
@@ -754,11 +792,11 @@ protected void writeFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOEx
* @author Mauro Bartolomeoli
*
*/
-
+
private class LineStringWriter extends HTMLImageMapFeatureWriter {
- // default buffer size (in screen coordinates)
- int buffer=2;
- /**
+ // default buffer size (in screen coordinates)
+ int buffer=2;
+ /**
* Creates a new LineStringWriter object.
*/
public LineStringWriter() {
@@ -768,57 +806,57 @@ public LineStringWriter() {
* The shape for lines is a poly.
*/
protected String getShape() throws IOException {
- return "poly";
+ return "poly";
}
-
+
/**
* Uses the supplied style to define line rendering.
- * Currently it gets stroke-width to define the buffer around the linestring.
+ * Currently it gets stroke-width to define the buffer around the linestring.
*/
protected void processSymbolizer(SimpleFeature ft, Rule rule,Symbolizer symbolizer) throws IOException{
- super.processSymbolizer(ft, rule,symbolizer);
- if(symbolizer instanceof LineSymbolizer) {
- buffer=SLD.width((LineSymbolizer)symbolizer);
- }
+ super.processSymbolizer(ft, rule,symbolizer);
+ if(symbolizer instanceof LineSymbolizer) {
+ buffer=SLD.width((LineSymbolizer)symbolizer);
+ }
}
-
+
/**
* Actually encodes the linestring.
*
- * @param geom line to encode
+ * @param geom line to encode
*
* @throws IOException if an error occures during encoding
*/
protected void writeGeometry(Geometry geom,StringBuffer buf) throws IOException {
- if(geom instanceof LineString) {
- LineString l = (LineString) geom;
-
- try {
- // transform buffer dimension to world coordinates
- double bufferMultiplier=worldToScreen.createInverse().getScaleX();
- // gets buffered linestring
- Geometry buffered=l.buffer(buffer*bufferMultiplier);
- if(buffered instanceof Polygon) {
- Polygon poly=(Polygon)decimate(buffered);
- if(poly!=null) {
- LineString shell = poly.getExteriorRing();
- if(shell!=null && shell.getCoordinates()!=null)
- writePathContent(shell.getCoordinates(),buf);
- else
- throw new IOException("Nothing to encode");
- } else
- throw new IOException("Nothing to encode");
- } else {
- throw new IOException("Impossible to encode: "+buffered);
- //TODO: what kind of geometry can a buffer operation
- // return?
- }
-
- } catch (NoninvertibleTransformException e) {
- throw new IOException(e.getMessage());
- }
- } else
- throw new IOException("Wrong geometry: it should be a LineString");
+ if(geom instanceof LineString) {
+ LineString l = (LineString) geom;
+
+ try {
+ // transform buffer dimension to world coordinates
+ double bufferMultiplier=worldToScreen.createInverse().getScaleX();
+ // gets buffered linestring
+ Geometry buffered=l.buffer(buffer*bufferMultiplier);
+ if(buffered instanceof Polygon) {
+ Polygon poly=(Polygon)decimate(buffered);
+ if(poly!=null) {
+ LineString shell = poly.getExteriorRing();
+ if(shell!=null && shell.getCoordinates()!=null)
+ writePathContent(shell.getCoordinates(),buf);
+ else
+ throw new IOException("Nothing to encode");
+ } else
+ throw new IOException("Nothing to encode");
+ } else {
+ throw new IOException("Impossible to encode: "+buffered);
+ //TODO: what kind of geometry can a buffer operation
+ // return?
+ }
+
+ } catch (NoninvertibleTransformException e) {
+ throw new IOException(e.getMessage());
+ }
+ } else
+ throw new IOException("Wrong geometry: it should be a LineString");
}
}
@@ -831,12 +869,12 @@ protected void writeGeometry(Geometry geom,StringBuffer buf) throws IOException
*/
public MultiLineStringWriter() {
}
-
+
/**
* Uses writeMultiFeature.
*/
protected void writeFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
- writeMultiFeature(ft, fts);
+ writeMultiFeature(ft, fts);
}
}
@@ -853,38 +891,38 @@ public PolygonWriter() {
* The shape for polygons is a poly.
*/
protected String getShape() throws IOException {
- return "poly";
+ return "poly";
}
-
+
/**
* Actually encodes the polygon.
*
- * @param geom the polygon to encode
+ * @param geom the polygon to encode
*
* @throws IOException if an error occures during encoding
*/
protected void writeGeometry(Geometry geom,StringBuffer buf) throws IOException {
- Polygon poly=null;
- if(geom instanceof Polygon) {
- poly=(Polygon)geom;
- // if we have any hole
- // we create a new polygon without holes
- // using the HolesRemover
- if(poly.getNumInteriorRing()>0) {
- poly=HolesRemover.removeHoles(poly,1.0/worldToScreen.getScaleX());
+ Polygon poly=null;
+ if(geom instanceof Polygon) {
+ poly=(Polygon)geom;
+ // if we have any hole
+ // we create a new polygon without holes
+ // using the HolesRemover
+ if(poly.getNumInteriorRing()>0) {
+ poly=HolesRemover.removeHoles(poly,1.0/worldToScreen.getScaleX());
}
- poly=(Polygon)decimate(poly);
- } else
- throw new IOException("Impossible to encode: "+geom);
+ poly=(Polygon)decimate(poly);
+ } else
+ throw new IOException("Impossible to encode: "+geom);
if(poly!=null) {
- LineString shell = poly.getExteriorRing();
- if(shell!=null && shell.getCoordinates()!=null)
- writePathContent(shell.getCoordinates(),buf);
- else
- throw new IOException("Nothing to encode");
+ LineString shell = poly.getExteriorRing();
+ if(shell!=null && shell.getCoordinates()!=null)
+ writePathContent(shell.getCoordinates(),buf);
+ else
+ throw new IOException("Nothing to encode");
} else
- throw new IOException("Nothing to encode");
-
+ throw new IOException("Nothing to encode");
+
}
}
@@ -897,125 +935,125 @@ protected void writeGeometry(Geometry geom,StringBuffer buf) throws IOException
*/
public MultiPolygonWriter() {
}
-
+
/**
* Uses writeMultiFeature.
*/
protected void writeFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
- writeMultiFeature(ft, fts);
+ writeMultiFeature(ft, fts);
}
-
-
+
+
}
-
+
/**
* FeatureWriter for multipolygon geometry features.
*/
private class GeometryCollectionWriter extends HTMLImageMapFeatureWriter {
-
- HTMLImageMapFeatureWriter delegateWriter=null;
-
+
+ HTMLImageMapFeatureWriter delegateWriter=null;
+
/**
* Creates a new MultiPolygonWriter object.
*/
public GeometryCollectionWriter() {
}
-
- /**
- * Encodes the GeometryCollection.
- *
- * The encoding is accomplished through many phases:
- * 1) reset writer state
- * 2) loops for all the geometries, with the following phases for every single geometry.
- * a) process supplied style
- * b) start feature encoding
- * c) pre geometry encoding
- * d) actual geometry encoding
- * e) post geometry encoding
- * f) end feature encoding
- * A delegate is used for many of these phases. The delegate is a specific FeatureWriter for the
- * single geometry, during the loop.
- * @param ft feature to encode
- * @param style style to use for the encoding
- * @param fts "cached" ftss matching the FeatureType of the feature
- * @throws IOException if an error occurs during encoding
- */
- protected void writeFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
- reset(ft);
-
- GeometryCollection geomCollection = (GeometryCollection) ft.getDefaultGeometry();
-
- for (int i = 0; i < geomCollection.getNumGeometries(); i++) {
- Geometry geom=geomCollection.getGeometryN(i);
- if(geom!=null) {
- Class<?> gtype = geom.getClass();
-
- // retrieves the right feature writer (based on the current geometry type)
- delegateWriter = (HTMLImageMapFeatureWriter) writers.get(gtype);
- if(processStyle(ft,fts)) {
- try {
- startElement(ft,"."+i);
- startGeometry(geom);
- writeGeometry(geom,buffer);
- endGeometry(geom);
- endElement(ft);
- commitBuffer();
- } catch(IOException e) {
- buffer=new StringBuffer();
- if(LOGGER.isLoggable(Level.WARNING))
- LOGGER.warning("Problems encoding shape: "+e.getMessage());
- } catch(Throwable t) {
- buffer=new StringBuffer();
- if(LOGGER.isLoggable(Level.SEVERE))
- LOGGER.severe("Problems encoding shape: "+t.getMessage());
- }
- }
- } else {
- buffer=new StringBuffer();
- if(LOGGER.isLoggable(Level.WARNING))
- LOGGER.warning("Problems encoding shape: null geometry");
- }
- }
-
- }
-
- protected String getShape() throws IOException {
- return delegateWriter.getShape();
- }
-
-
- /**
+
+ /**
+ * Encodes the GeometryCollection.
+ *
+ * The encoding is accomplished through many phases:
+ * 1) reset writer state
+ * 2) loops for all the geometries, with the following phases for every single geometry.
+ * a) process supplied style
+ * b) start feature encoding
+ * c) pre geometry encoding
+ * d) actual geometry encoding
+ * e) post geometry encoding
+ * f) end feature encoding
+ * A delegate is used for many of these phases. The delegate is a specific FeatureWriter for the
+ * single geometry, during the loop.
+ * @param ft feature to encode
+ * @param style style to use for the encoding
+ * @param fts "cached" ftss matching the FeatureType of the feature
+ * @throws IOException if an error occurs during encoding
+ */
+ protected void writeFeature(SimpleFeature ft,FeatureTypeStyle[] fts) throws IOException {
+ reset(ft);
+
+ GeometryCollection geomCollection = (GeometryCollection) ft.getDefaultGeometry();
+
+ for (int i = 0; i < geomCollection.getNumGeometries(); i++) {
+ Geometry geom=geomCollection.getGeometryN(i);
+ if(geom!=null) {
+ Class<?> gtype = geom.getClass();
+
+ // retrieves the right feature writer (based on the current geometry type)
+ delegateWriter = (HTMLImageMapFeatureWriter) writers.get(gtype);
+ if(processStyle(ft,fts)) {
+ try {
+ startElement(ft,"."+i);
+ startGeometry(geom);
+ writeGeometry(geom,buffer);
+ endGeometry(geom);
+ endElement(ft);
+ commitBuffer();
+ } catch(IOException e) {
+ buffer=new StringBuffer();
+ if(LOGGER.isLoggable(Level.WARNING))
+ LOGGER.warning("Problems encoding shape: "+e.getMessage());
+ } catch(Throwable t) {
+ buffer=new StringBuffer();
+ if(LOGGER.isLoggable(Level.SEVERE))
+ LOGGER.severe("Problems encoding shape: "+t.getMessage());
+ }
+ }
+ } else {
+ buffer=new StringBuffer();
+ if(LOGGER.isLoggable(Level.WARNING))
+ LOGGER.warning("Problems encoding shape: null geometry");
+ }
+ }
+
+ }
+
+ protected String getShape() throws IOException {
+ return delegateWriter.getShape();
+ }
+
+
+ /**
* Analyze the supplied style and process any matching rule.
- *
+ *
* @param ft feature to which the style is going to be applied
* @param style style to process
* @param ftsList cached fts matching the feature
* @return true if the style filters "accept" the feature
* @throws IOException if an error occurs during the process
*/
- protected boolean processStyle(SimpleFeature ft,FeatureTypeStyle[] ftsList)
+ protected boolean processStyle(SimpleFeature ft,FeatureTypeStyle[] ftsList)
throws IOException {
- if(delegateWriter.processStyle(ft, ftsList)) {
- Iterator<String> iter=delegateWriter.extraAttributes.keySet().iterator();
- while(iter.hasNext()) {
- String attrName=(String)iter.next();
- extraAttributes.put(attrName,delegateWriter.extraAttributes.get(attrName));
- }
- return true;
- } else
- return false;
- }
-
+ if(delegateWriter.processStyle(ft, ftsList)) {
+ Iterator<String> iter=delegateWriter.extraAttributes.keySet().iterator();
+ while(iter.hasNext()) {
+ String attrName=(String)iter.next();
+ extraAttributes.put(attrName,delegateWriter.extraAttributes.get(attrName));
+ }
+ return true;
+ } else
+ return false;
+ }
+
/**
* Actually write the geometry (through the delegate).
*/
- protected void writeGeometry(Geometry geom,StringBuffer buf) throws IOException {
- if(geom!=null)
- delegateWriter.writeGeometry(geom,buf);
- else
- throw new IOException("null geometry");
- }
-
-
+ protected void writeGeometry(Geometry geom,StringBuffer buf) throws IOException {
+ if(geom!=null)
+ delegateWriter.writeGeometry(geom,buf);
+ else
+ throw new IOException("null geometry");
+ }
+
+
}
}
View
6 ...src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BasicPolygons.txt
@@ -1,5 +1,5 @@
<map name="BasicPolygons">
-<area shape="poly" id="BasicPolygons.1107531493630.0" coords="150,514 300,429 450,514 300,600 150,514"/>
-<area shape="poly" id="BasicPolygons.1107531493643.0" coords="0,0 450,0 450,257 0,257 0,0"/>
-<area shape="poly" id="BasicPolygons.1107531493644.0" coords="150,86 600,86 600,343 150,343 150,86"/>
+<area shape="poly" id="BasicPolygons.1107531493630.0" coords="150,514,300,429,450,514,300,600,150,514"/>
+<area shape="poly" id="BasicPolygons.1107531493643.0" coords="0,0,450,0,450,257,0,257,0,0"/>
+<area shape="poly" id="BasicPolygons.1107531493644.0" coords="150,86,600,86,600,343,150,343,150,86"/>
</map>
View
4 ...c/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCenters.txt
@@ -1,4 +1,4 @@
<map name="BuildingCenters">
-<area shape="circle" id="BuildingCenters.1107531643027" coords="0,600,6" title="ADDRESS: 123 Main Street"/>
-<area shape="circle" id="BuildingCenters.1107531643028" coords="600,0,6" title="ADDRESS: 215 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643027" coords="0,600,8" title="ADDRESS: 123 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643028" coords="600,0,8" title="ADDRESS: 215 Main Street"/>
</map>
View
4 .../test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCenters2.txt
@@ -1,4 +1,4 @@
<map name="BuildingCenters">
-<area shape="circle" id="BuildingCenters.1107531643027" coords="0,600,60" title="ADDRESS: 123 Main Street"/>
-<area shape="circle" id="BuildingCenters.1107531643028" coords="600,0,60" title="ADDRESS: 215 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643027" coords="0,600,62" title="ADDRESS: 123 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643028" coords="600,0,62" title="ADDRESS: 215 Main Street"/>
</map>
View
4 .../test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCenters3.txt
@@ -1,4 +1,4 @@
<map name="BuildingCenters">
-<area shape="circle" id="BuildingCenters.1107531643027" coords="0,600,10" title="ADDRESS: 123 Main Street"/>
-<area shape="circle" id="BuildingCenters.1107531643028" coords="600,0,10" title="ADDRESS: 215 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643027" coords="0,600,22" title="ADDRESS: 123 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643028" coords="600,0,22" title="ADDRESS: 215 Main Street"/>
</map>
View
4 .../test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCenters4.txt
@@ -1,4 +1,4 @@
<map name="BuildingCenters">
-<area shape="circle" id="BuildingCenters.1107531643027" coords="300,300,20" title="ADDRESS: 123 Main Street"/>
-<area shape="circle" id="BuildingCenters.1107531643028" coords="300,300,20" title="ADDRESS: 215 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643027" coords="300,300,22" title="ADDRESS: 123 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643028" coords="300,300,22" title="ADDRESS: 215 Main Street"/>
</map>
View
6 ...ources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/BuildingCentersMultiPoint.txt
@@ -1,5 +1,5 @@
<map name="BuildingCentersMultiPoint">
-<area shape="circle" id="BuildingCenters.1107531643027.0" coords="0,600,6" title="ADDRESS: 123 Main Street"/>
-<area shape="circle" id="BuildingCenters.1107531643027.1" coords="500,300,6" title="ADDRESS: 123 Main Street"/>
-<area shape="circle" id="BuildingCenters.1107531643028.0" coords="600,0,6" title="ADDRESS: 215 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643027.0" coords="0,600,8" title="ADDRESS: 123 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643027.1" coords="500,300,8" title="ADDRESS: 123 Main Street"/>
+<area shape="circle" id="BuildingCenters.1107531643028.0" coords="600,0,8" title="ADDRESS: 215 Main Street"/>
</map>
View
6 .../test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/CollectionSample.txt
@@ -1,5 +1,5 @@
<map name="CollectionSample">
-<area shape="circle" id="Sample.1.0" coords="488,120,6" href="123 Main Street"/>
-<area shape="poly" id="Sample.1.1" coords="93,464 149,384 261,264 411,64 413,63 414,65 415,67 416,69 416,72 416,75 416,78 416,82 416,85 416,88 416,91 415,93 414,95 414,96 264,296 151,416 95,496 1,616 0,617 -2,615 -3,613 -3,611 -4,608 -4,605 -4,602 -4,598 -4,595 -4,592 -3,589 -3,587 -2,585 -1,584 93,464" href="123 Main Street"/>
-<area shape="circle" id="Sample.2.0" coords="600,0,6" href="215 Main Street"/>
+<area shape="circle" id="Sample.1.0" coords="488,120,8" href="123 Main Street"/>
+<area shape="poly" id="Sample.1.1" coords="93,464,149,384,261,264,411,64,413,63,414,65,415,67,416,69,416,72,416,75,416,78,416,82,416,85,416,88,416,91,415,93,414,95,414,96,264,296,151,416,95,496,1,616,0,617,-2,615,-3,613,-3,611,-4,608,-4,605,-4,602,-4,598,-4,595,-4,592,-3,589,-3,587,-2,585,-1,584,93,464" href="123 Main Street"/>
+<area shape="circle" id="Sample.2.0" coords="600,0,8" href="215 Main Street"/>
</map>
View
2  .../test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/PolygonWithHoles.txt
@@ -1,3 +1,3 @@
<map name="PolygonWithHoles">
-<area shape="poly" id="BasicPolygons.1107531493630.0" coords="0,600 600,600 600,0 450,150 450,450 150,450 150,150 450,150 600,0 0,0 0,600"/>
+<area shape="poly" id="BasicPolygons.1107531493630.0" coords="0,600,600,600,600,0,450,150,450,450,150,450,150,150,450,150,600,0,0,0,0,600"/>
</map>
View
2  ...esources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/PolygonWithSkippedHoles.txt
@@ -1,3 +1,3 @@
<map name="PolygonWithSkippedHoles">
-<area shape="poly" id="BasicPolygons.1107531493630.0" coords="0,600 600,600 600,0 0,0 0,600"/>
+<area shape="poly" id="BasicPolygons.1107531493630.0" coords="0,600,600,600,600,0,0,0,0,600"/>
</map>
View
2  .../test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/ProjectedPolygon.txt
@@ -1,3 +1,3 @@
<map name="ProjectedPolygon">
-<area shape="poly" id="ProjectedPolygon.1.0" coords="589,437 419,8 344,29 442,56 25,325 277,586 446,559 589,437"/>
+<area shape="poly" id="ProjectedPolygon.1.0" coords="589,437,419,8,344,29,442,56,25,325,277,586,446,559,589,437"/>
</map>
View
10 .../src/test/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/RoadSegments.txt
@@ -1,7 +1,7 @@
<map name="RoadSegments">
-<area shape="poly" id="RoadSegments.1107532045088.0" coords="70,331 113,306 199,268 313,206 315,206 316,206 317,208 318,209 318,210 318,212 318,213 318,214 318,216 317,217 317,218 315,219 201,282 115,319 73,344 1,382 0,382 -2,381 -3,379 -4,378 -4,377 -4,376 -4,374 -4,373 -4,372 -3,371 -3,370 -1,368 70,331"/>
-<area shape="poly" id="RoadSegments.1107532045089.0" coords="399,168 499,118 500,118 502,119 503,121 504,122 504,123 504,124 504,126 504,127 503,128 503,130 502,130 501,132 401,182 315,219 314,219 312,219 312,218 311,217 311,215 310,214 310,213 310,211 311,210 311,209 311,208 312,207 313,206 399,168"/>
-<area shape="poly" id="RoadSegments.1107532045089.0" coords="510,-1 511,-3 511,-4 511,-5 512,-6 514,-7 515,-7 517,-6 517,-5 518,-4 518,-3 518,-1 518,0 518,1 504,126 504,128 503,129 503,130 502,131 501,132 499,132 498,131 497,130 497,129 496,128 496,126 496,125 496,124 510,-1"/>
-<area shape="poly" id="RoadSegments.1107532045090.0" coords="399,168 499,118 599,68 600,68 602,69 603,71 604,72 604,73 604,74 604,76 604,77 603,78 603,80 602,80 601,82 501,132 401,182 315,219 314,219 312,219 312,218 311,217 311,215 310,214 310,213 310,211 311,210 311,209 311,208 312,207 313,206 399,168"/>
-<area shape="poly" id="RoadSegments.1107532045091.0" coords="196,275 196,274 196,272 197,271 197,270 198,269 199,268 201,268 202,269 203,270 203,271 204,272 204,274 204,275 204,600 204,601 204,603 203,604 203,605 202,606 201,607 199,607 198,606 197,605 197,604 196,603 196,601 196,600 196,275"/>
+<area shape="poly" id="RoadSegments.1107532045088.0" coords="70,331,113,306,199,268,313,206,315,206,316,206,317,208,318,209,318,210,318,212,318,213,318,214,318,216,317,217,317,218,315,219,201,282,115,319,73,344,1,382,0,382,-2,381,-3,379,-4,378,-4,377,-4,376,-4,374,-4,373,-4,372,-3,371,-3,370,-1,368,70,331"/>
+<area shape="poly" id="RoadSegments.1107532045089.0" coords="399,168,499,118,500,118,502,119,503,121,504,122,504,123,504,124,504,126,504,127,503,128,503,130,502,130,501,132,401,182,315,219,314,219,312,219,312,218,311,217,311,215,310,214,310,213,310,211,311,210,311,209,311,208,312,207,313,206,399,168"/>
+<area shape="poly" id="RoadSegments.1107532045089.0" coords="510,-1,511,-3,511,-4,511,-5,512,-6,514,-7,515,-7,517,-6,517,-5,518,-4,518,-3,518,-1,518,0,518,1,504,126,504,128,503,129,503,130,502,131,501,132,499,132,498,131,497,130,497,129,496,128,496,126,496,125,496,124,510,-1"/>
+<area shape="poly" id="RoadSegments.1107532045090.0" coords="399,168,499,118,599,68,600,68,602,69,603,71,604,72,604,73,604,74,604,76,604,77,603,78,603,80,602,80,601,82,501,132,401,182,315,219,314,219,312,219,312,218,311,217,311,215,310,214,310,213,310,211,311,210,311,209,311,208,312,207,313,206,399,168"/>
+<area shape="poly" id="RoadSegments.1107532045091.0" coords="196,275,196,274,196,272,197,271,197,270,198,269,199,268,201,268,202,269,203,270,203,271,204,272,204,274,204,275,204,600,204,601,204,603,203,604,203,605,202,606,201,607,199,607,198,606,197,605,197,604,196,603,196,601,196,600,196,275"/>
</map>
View
6 ...t/resources/org/vfny/geoserver/wms/responses/map/htmlimagemap/test-data/results/RoadSegmentsFiltered.txt
@@ -1,5 +1,5 @@
<map name="RoadSegments">
-<area shape="poly" id="RoadSegments.1107532045088.0" coords="70,331 113,306 199,268 313,206 315,206 316,206 317,208 318,209 318,210 318,212 318,213 318,214 318,216 317,217 317,218 315,219 201,282 115,319 73,344 1,382 0,382 -2,381 -3,379 -4,378 -4,377 -4,376 -4,374 -4,373