📄 kmlwriter.java
字号:
/* 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.kml;
import java.awt.Color;
import java.awt.Paint;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.logging.Logger;
import javax.media.jai.util.Range;
import javax.servlet.http.HttpServletRequest;
import javax.xml.transform.TransformerException;
import org.geoserver.template.FeatureWrapper;
import org.geoserver.template.GeoServerTemplateLoader;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.data.DataSourceException;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.FeatureType;
import org.geotools.feature.GeometryAttributeType;
import org.geotools.feature.IllegalAttributeException;
import org.geotools.geometry.jts.JTS;
import org.geotools.gml.producer.GeometryTransformer;
import org.geotools.map.MapLayer;
import org.geotools.referencing.CRS;
import org.geotools.renderer.style.LineStyle2D;
import org.geotools.renderer.style.MarkStyle2D;
import org.geotools.renderer.style.PolygonStyle2D;
import org.geotools.renderer.style.SLDStyleFactory;
import org.geotools.renderer.style.Style2D;
import org.geotools.renderer.style.TextStyle2D;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.Mark;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.Style;
import org.geotools.styling.Symbolizer;
import org.geotools.styling.TextSymbolizer;
import org.geotools.util.NumberRange;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.Expression;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.vfny.geoserver.global.GeoServer;
import org.vfny.geoserver.wms.WMSMapContext;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* Writer for KML/KMZ (Keyhole Markup Language) files. Normaly controled by an
* EncodeKML instance, this class handles the styling information and ensures
* that the geometries produced match the pseudo GML expected by GE.
*
* @REVISIT: Once this is fully working, revisit as an extention to
* TransformerBase
* @author James Macgill
* @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $
* @author $Author: Simone Giannecchini (simboss1@gmail.com) $
* @author Brent Owens
*
* @deprecated use {@link KMLTransformer}.
*/
public class KMLWriter extends OutputStreamWriter {
private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger(KMLWriter.class
.getPackage().getName());
/**
* a number formatter set up to write KML legible numbers
*/
private static DecimalFormat formatter;
/**
* The template configuration
*/
private static Configuration templateConfig;
/**
* Resolves the FeatureTypeStyle info per feature into a Style2D object.
*/
private SLDStyleFactory styleFactory = new SLDStyleFactory();
// TODO: calcuate a real value based on image size to bbox ratio, as image
// size has no meanining for KML yet this is a fudge.
private double scaleDenominator = 1;
/** Tolerance used to compare doubles for equality */
private static final double TOLERANCE = 1e-6;
/**
* The CRS of the data we are querying. It is a bit of a hack because
* sometimes when we grab the CRS from the feature itself, we get null. This
* variable is paired with setSourceCrs() so EncodeKML can can use the
* feature type's schema to set the CRS.
*/
private CoordinateReferenceSystem sourceCrs;
/**
* Handles the outputing of geometries as GML
*/
private GeometryTransformer transformer;
static {
Locale locale = new Locale("en", "US");
DecimalFormatSymbols decimalSymbols = new DecimalFormatSymbols(locale);
decimalSymbols.setDecimalSeparator('.');
formatter = new DecimalFormat();
formatter.setDecimalFormatSymbols(decimalSymbols);
// do not group
formatter.setGroupingSize(0);
// do not show decimal separator if it is not needed
formatter.setDecimalSeparatorAlwaysShown(false);
formatter.setDecimalFormatSymbols(null);
// set default number of fraction digits
formatter.setMaximumFractionDigits(5);
// minimun fraction digits to 0 so they get not rendered if not needed
formatter.setMinimumFractionDigits(0);
// initialize the template engine, this is static to maintain a cache
// over instantiations of kml writer
templateConfig = new Configuration();
templateConfig.setObjectWrapper(new FeatureWrapper());
}
/** Holds the map layer set, styling info and area of interest bounds */
private WMSMapContext mapContext;
/**
* Creates a new KMLWriter object.
*
* @param out
* OutputStream to write the KML into
* @param config
* WMSMapContext describing the map to be generated.
*/
public KMLWriter(OutputStream out, WMSMapContext mapContext) {
super(out, Charset.forName("UTF-8"));
this.mapContext = mapContext;
transformer = new GeometryTransformer();
// transformer.setUseDummyZ(true);
transformer.setOmitXMLDeclaration(true);
transformer.setNamespaceDeclarationEnabled(true);
GeoServer config = mapContext.getRequest().getGeoServer();
transformer.setNumDecimals(config.getNumDecimals());
}
/**
* Sets the maximum number of digits allowed in the fraction portion of a
* number.
*
* @param numDigits
* @see NumberFormat#setMaximumFractionDigits
*/
public void setMaximunFractionDigits(int numDigits) {
formatter.setMaximumFractionDigits(numDigits);
}
/**
* Gets the maximum number of digits allowed in the fraction portion of a
* number.
*
* @return int numDigits
* @see NumberFormat#getMaximumFractionDigits
*/
public int getMaximunFractionDigits() {
return formatter.getMaximumFractionDigits();
}
/**
* Sets the minimum number of digits allowed in the fraction portion of a
* number.
*
* @param numDigits
* @see NumberFormat#setMinimumFractionDigits
*/
public void setMinimunFractionDigits(int numDigits) {
formatter.setMinimumFractionDigits(numDigits);
}
/*
* Sets the minimum number of digits allowed in the fraction portion of a
* number.
*
* @param numDigits
*
* @see NumberFormat#getMinimumFractionDigits
*/
public int getMinimunFractionDigits() {
return formatter.getMinimumFractionDigits();
}
public void setRequestedScale(double scale) {
scaleDenominator = scale;
}
public void setSourceCrs(CoordinateReferenceSystem crs) {
sourceCrs = crs;
}
/**
* Formated version of standard write double
*
* @param d
* The double to format and write out.
*
* @throws IOException
*/
public void write(double d) throws IOException {
write(formatter.format(d));
}
/**
* Convinience method to add a newline char to the output
*
* @throws IOException
*/
public void newline() throws IOException {
super.write('\n');
}
public void writeFeaturesAsRaster(final FeatureCollection features,
final MapLayer layer, final int order) throws IOException,
AbortedException {
Style style = layer.getStyle();
try {
FeatureType featureType = features.getSchema();
setUpWriterHandler(featureType);
FeatureTypeStyle[] fts = style.getFeatureTypeStyles();
processStylersRaster(features, fts, layer, order);
LOGGER.fine("encoded " + featureType.getTypeName().toString());
} catch (NoSuchElementException ex) {
throw new DataSourceException(ex.getMessage(), ex);
} catch (IllegalAttributeException ex) {
throw new DataSourceException(ex.getMessage(), ex);
}
}
public void writeFeaturesAsVectors(final FeatureCollection features,
final MapLayer layer) throws IOException, AbortedException {
Style style = layer.getStyle();
try {
FeatureType featureType = features.getSchema();
setUpWriterHandler(featureType);
FeatureTypeStyle[] fts = style.getFeatureTypeStyles();
processStylersVector(features, fts, layer);
LOGGER.fine("encoded " + featureType.getTypeName().toString());
} catch (NoSuchElementException ex) {
throw new DataSourceException(ex.getMessage(), ex);
} catch (IllegalAttributeException ex) {
throw new DataSourceException(ex.getMessage(), ex);
}
}
public void writeCoverages(final FeatureCollection features,
final MapLayer layer) throws IOException, AbortedException {
Style style = layer.getStyle();
try {
FeatureType featureType = features.getSchema();
setUpWriterHandler(featureType);
FeatureTypeStyle[] fts = style.getFeatureTypeStyles();
processStylersCoverage(features, fts, layer);
LOGGER.fine("encoded " + featureType.getTypeName().toString());
} catch (NoSuchElementException ex) {
throw new DataSourceException(ex.getMessage(), ex);
} catch (IllegalAttributeException ex) {
throw new DataSourceException(ex.getMessage(), ex);
}
}
/**
* Write all the features in a collection which pass the rules in the
* provided Style object.
*
* @TODO: support Name and Description information
*/
/*
* public void writeFeatures(final FeatureCollection features, final
* MapLayer layer, final int order, final boolean kmz, final boolean
* vectorResult) throws IOException, AbortedException { Style style =
* layer.getStyle();
*
* try { FeatureType featureType = features.getSchema();
*
* setUpWriterHandler(featureType); FeatureTypeStyle[] fts =
* style.getFeatureTypeStyles(); if (!kmz) processStylers(features, fts,
* layer, order); else processStylersKMZ(features, fts, layer, order,
* vectorResult);
*
*
* LOGGER.fine(new StringBuffer("encoded
* ").append(featureType.getTypeName()).toString()); } catch
* (NoSuchElementException ex) { throw new
* DataSourceException(ex.getMessage(), ex); } catch
* (IllegalAttributeException ex) { throw new
* DataSourceException(ex.getMessage(), ex); } }
*/
/**
* Start a new KML folder. From the spec 2.0: A top-level, optional tag used
* to structure hierarchical arrangement of other folders, placemarks,
* ground overlays, and screen overlays. Use this tag to structure and
* organize your information in the Google Earth client.
*
* In this context we should be using a Folder per map layer.
*
* @param name
* A String to label this folder with, if null the name tag will
* be ommited
* @param description
* Supplies descriptive information. This description appears in
* the Places window when the user clicks on the folder or ground
* overlay, and in a pop-up window when the user clicks on either
* the Placemark name in the Places window, or the placemark
* icon. The description element supports plain text as well as
* HTML formatting. A valid URL string for the World Wide Web is
* automatically converted to a hyperlink to that URL (e.g.
* http://www.google.com). if null the description tag will be
* ommited
*/
public void startFolder(String name, String description) throws IOException {
write("<Folder>");
if (name != null) {
write("<name>" + name + "</name>");
}
if (description != null) {
write("<description>" + description + "</description>");
}
}
public void startDocument(String name, String description)
throws IOException {
write("<Document>");
if (name != null) {
write("<name>" + name + "</name>");
}
if (description != null) {
write("<description>" + description + "</description>");
}
}
public void endFolder() throws IOException {
write("</Folder>");
}
public void endDocument() throws IOException {
write("</Document>");
}
/**
* Gather any information needed to write the KML document.
*
* @TODO: support writing of 'Schema' tags based on featureType
*/
private void setUpWriterHandler(FeatureType featureType) throws IOException {
String typeName = featureType.getTypeName();
/*
* REVISIT: To use attributes properly we need to be using the 'schema'
* part of KML to contain custom data..
*/
List atts = new ArrayList(0); // config.getAttributes(typeName);
}
/**
* Write out the geometry. Contains workaround for the fact that KML2.0 does
* not support multipart geometries in the same way that GML does.
*
* @param geom
* The Geometry to be encoded, multi part geometries will be
* written as a sequence.
* @param trans
* A GeometryTransformer to produce the gml output, its output is
* post processed to remove gml namespace prefixes.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -