⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 kmlwriter.java

📁 电子地图服务器,搭建自己的地图服务
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/* 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 + -