📄 kmlvectortransformer.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 com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineSegment;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import org.geoserver.ows.util.RequestUtils;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.FeatureType;
import org.geotools.feature.type.DateUtil;
import org.geotools.geometry.jts.JTS;
import org.geotools.map.MapLayer;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
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.ExternalGraphic;
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.Rule;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.styling.Symbolizer;
import org.geotools.styling.TextSymbolizer;
import org.geotools.util.Converters;
import org.geotools.util.NumberRange;
import org.geotools.xml.SimpleBinding;
import org.geotools.xml.transform.TransformerBase;
import org.geotools.xml.transform.Translator;
import org.geotools.xs.bindings.XSDateBinding;
import org.geotools.xs.bindings.XSDateTimeBinding;
import org.geotools.xs.bindings.XSTimeBinding;
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 org.vfny.geoserver.wms.responses.featureInfo.FeatureTemplate;
import org.vfny.geoserver.wms.responses.featureInfo.FeatureTimeTemplate;
import org.vfny.geoserver.wms.responses.map.kml.KMLGeometryTransformer.KMLGeometryTranslator;
import org.xml.sax.ContentHandler;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Transforms a feature collection to a kml document consisting of nested
* "Style" and "Placemark" elements for each feature in the collection.
* A new transfomer must be instantianted for each feature collection,
* the feature collection provided to the translator is supposed to be
* the one coming out of the MapLayer
* <p>
* Usage:
* </p>
* @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
*
*/
public class KMLVectorTransformer extends KMLTransformerBase {
/**
* logger
*/
static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.kml");
/**
* Tolerance used to compare doubles for equality
*/
static final double TOLERANCE = 1e-6;
/**
* The map context
*/
protected final WMSMapContext mapContext;
/**
* The map layer being transformed
*/
protected final MapLayer mapLayer;
/**
* The scale denominator.
*
* 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.
*/
double scaleDenominator = 1;
NumberRange scaleRange = new NumberRange(scaleDenominator, scaleDenominator);
/**
* used to create 2d style objects for features
*/
SLDStyleFactory styleFactory = new SLDStyleFactory();
/**
* Feature template, cached for performance reasons
*/
FeatureTemplate template = new FeatureTemplate();
/**
* list of formats which correspond to the default formats in which
* freemarker outputs dates when a user calls the ?datetime(),?date(),?time()
* fuctions.
*/
static List/*<SimpleDateFormat>*/ dtformats = new ArrayList();
static List/*<SimpleDateFormat>*/ dformats = new ArrayList();
static List/*<SimpleDateFormat>*/ tformats = new ArrayList();
static {
//add default freemarker ones first since they are likely to be used
// first, the order of this list matters.
dtformats.add( FeatureTemplate.DATETIME_FORMAT );
addFormats(dtformats,"dd%MM%yy hh:mm:ss" );
addFormats(dtformats,"MM%dd%yy hh:mm:ss" );
//addFormats(formats,"yy%MM%dd hh:mm:ss" );
addFormats(dtformats,"dd%MMM%yy hh:mm:ss" );
addFormats(dtformats,"MMM%dd%yy hh:mm:ss" );
//addFormats(formats,"yy%MMM%dd hh:mm:ss" );
addFormats(dtformats,"dd%MM%yy hh:mm" );
addFormats(dtformats,"MM%dd%yy hh:mm" );
//addFormats(formats,"yy%MM%dd hh:mm" );
addFormats(dtformats,"dd%MMM%yy hh:mm" );
addFormats(dtformats,"MMM%dd%yy hh:mm" );
//addFormats(formats,"yy%MMM%dd hh:mm" );
dformats.add( FeatureTemplate.DATE_FORMAT );
addFormats(dformats,"dd%MM%yy" );
addFormats(dformats,"MM%dd%yy" );
//addFormats(formats,"yy%MM%dd" );
addFormats(dformats,"dd%MMM%yy" );
addFormats(dformats,"MMM%dd%yy" );
//addFormats(formats,"yy%MMM%dd" );
tformats.add( FeatureTemplate.TIME_FORMAT );
}
static void addFormats( List formats, String pattern ) {
formats.add( new SimpleDateFormat( pattern.replaceAll("%","-" ) ) );
formats.add( new SimpleDateFormat( pattern.replaceAll("%","/" ) ) );
formats.add( new SimpleDateFormat( pattern.replaceAll("%","." ) ) );
formats.add( new SimpleDateFormat( pattern.replaceAll("%"," " ) ) );
formats.add( new SimpleDateFormat( pattern.replaceAll("%","," ) ) );
}
public KMLVectorTransformer(WMSMapContext mapContext, MapLayer mapLayer) {
this.mapContext = mapContext;
this.mapLayer = mapLayer;
setNamespaceDeclarationEnabled(false);
}
/**
* Sets the scale denominator.
*/
public void setScaleDenominator(double scaleDenominator) {
this.scaleDenominator = scaleDenominator;
}
public Translator createTranslator(ContentHandler handler) {
return new KMLTranslator(handler);
}
protected class KMLTranslator extends KMLTranslatorSupport {
/**
* Geometry transformer
*/
Translator geometryTranslator;
public KMLTranslator(ContentHandler contentHandler) {
super(contentHandler);
KMLGeometryTransformer geometryTransformer = new KMLGeometryTransformer();
//geometryTransformer.setUseDummyZ( true );
geometryTransformer.setOmitXMLDeclaration(true);
geometryTransformer.setNamespaceDeclarationEnabled(true);
GeoServer config = mapContext.getRequest().getGeoServer();
geometryTransformer.setNumDecimals(config.getNumDecimals());
geometryTranslator = geometryTransformer.createTranslator(contentHandler);
}
public void encode(Object o) throws IllegalArgumentException {
FeatureCollection features = (FeatureCollection) o;
FeatureType featureType = features.getSchema();
if (isStandAlone()) {
start( "kml" );
}
//start the root document, name it the name of the layer
start("Document");
element("name", mapLayer.getTitle());
//get the styles for hte layer
FeatureTypeStyle[] featureTypeStyles = filterFeatureTypeStyles(mapLayer.getStyle(),
featureType);
// encode the schemas (kml 2.2)
encodeSchemas(features);
// encode the layers
encode(features, featureTypeStyles);
//encode the legend
//encodeLegendScreenOverlay();
end("Document");
if ( isStandAlone() ) {
end( "kml" );
}
}
/**
* Encodes the <Schema> element in kml 2.2
* @param featureTypeStyles
*/
protected void encodeSchemas(FeatureCollection featureTypeStyles) {
// the code is at the moment in KML3VectorTransformer
}
protected void encode(FeatureCollection features, FeatureTypeStyle[] styles) {
//grab a feader and process
FeatureIterator reader = features.features();
try {
while (reader.hasNext()) {
Feature feature = (Feature) reader.next();
try {
encode(feature, styles);
} catch (Throwable t) {
//TODO: perhaps rethrow hte exception
String msg = "Failure tranforming feature to KML:" + feature.getID();
LOGGER.log(Level.WARNING, msg, t);
}
}
} finally {
//make sure we always close
features.close(reader);
}
}
protected void encode(Feature feature, FeatureTypeStyle[] styles) {
//get the feature id
String featureId = featureId(feature);
//start the document
//start("Document");
// element("name", featureId);
// element("title", mapLayer.getTitle());
//encode the styles, keep track of any labels provided by the
// styles
if ( encodeStyle(feature, styles) ) {
encodePlacemark(feature,styles);
}
//end("Document");
}
/**
* Encodes the provided set of rules as KML styles.
*/
protected boolean encodeStyle(Feature feature, FeatureTypeStyle[] styles) {
//encode hte Line/Poly styles
List symbolizerList = new ArrayList();
for ( int j = 0; j < styles.length ; j++ ) {
Rule[] rules = filterRules(styles[j], feature);
for (int i = 0; i < rules.length; i++) {
symbolizerList.addAll(Arrays.asList(rules[i].getSymbolizers()));
}
}
if ( !symbolizerList.isEmpty() ) {
//start the style
start("Style",
KMLUtils.attributes(new String[] { "id", "GeoServerStyle" + feature.getID() }));
//encode the icon
encodeIconStyle(feature, styles);
Symbolizer[] symbolizers = (Symbolizer[]) symbolizerList.toArray(new Symbolizer[symbolizerList.size()]);
encodeStyle(feature, symbolizers);
//end the style
end("Style");
//return true to specify that the feature has a style
return true;
}
else {
//dont encode
return false;
}
}
/**
* Encodes an IconStyle for a feature.
*/
protected void encodeIconStyle(Feature feature, FeatureTypeStyle[] styles ) {
//encode the style for the icon
//start IconStyle
start("IconStyle");
//make transparent if they didn't ask for attributes
if (!mapContext.getRequest().getKMattr()) {
encodeColor("00ffffff");
}
//figure out if line or polygon
boolean line = feature.getDefaultGeometry() != null &&
(feature.getDefaultGeometry() instanceof LineString
|| feature.getDefaultGeometry() instanceof MultiLineString);
boolean poly = feature.getDefaultGeometry() != null &&
(feature.getDefaultGeometry() instanceof Polygon
|| feature.getDefaultGeometry() instanceof MultiPolygon);
//if line or polygon scale the label
if ( line ) {
element( "scale", "0.7");
} else if ( poly ) {
element( "scale", "1.4" );
}
//start Icon
start("Icon");
if ( line || poly ) {
String imageURL;
try {
URL requestURL = new URL(mapContext.getRequest().getBaseUrl());
imageURL = requestURL.getProtocol() + "://" + requestURL.getHost() + ":" + requestURL.getPort();
imageURL += "/geoserver/" + (poly ? "icon-poly.png" : "icon-line.png");
} catch (MalformedURLException mue){
imageURL = "http://maps.google.com/mapfiles/kml/pal3/icon61.png";
}
element("href", imageURL);
}
else {
//do nothing, this is handled by encodePointStyle
}
end("Icon");
//end IconStyle
end("IconStyle");
}
/**
* Encodes the provided set of symbolizers as KML styles.
*/
protected void encodeStyle(Feature feature, Symbolizer[] symbolizers) {
// look for line symbolizers, if there is any, we should tell the
// polygon style to have an outline
boolean forceOutline = false;
for (int i = 0; i < symbolizers.length; i++) {
if (symbolizers[i] instanceof LineSymbolizer) {
forceOutline = true;
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -