/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.io;
import com.vividsolutions.jts.geom.*;
import java.io.*;
/**
* Writes or creates a formatted string containing the GML
* representation of a JTS Geometry.
* Supports a user-defined line prefix and a user-defined maximum number of coordinates per line.
* Indents components of Geometries to provide a nicely-formatted representation.
*/
public class GMLGeometryWriter
{
/**
* Returns a <code>String</code> of repeated characters.
*
*@param ch the character to repeat
*@param count the number of times to repeat the character
*@return a <code>String</code> of characters
*/
private static String stringOfChar(char ch, int count) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < count; i++) {
buf.append(ch);
}
return buf.toString();
}
private final int INDENT_SIZE = 2;
// these could be make settable
private static final String coordinateSeparator = ",";
private static final String tupleSeparator = " ";
private String linePrefix = null;
private int maxCoordinatesPerLine = 10;
private String srsName = null;
private String gid = null;
public GMLGeometryWriter() {
}
public void setLinePrefix(String linePrefix)
{
this.linePrefix = linePrefix;
}
/**
* Sets the <code>srsName</code> attribute to be output on the Geometry element.
* If <code>null</code> no attribute will be output.
* @param srsName
*/
public void setSRSName(String srsName)
{
this.srsName = srsName;
}
/**
* Sets the <code>gid</code> attribute to be output on the Geometry element.
* If <code>null</code> no attribute will be output.
* @param gid the geographic identifier
*/
public void setGID(String gid)
{
this.gid = gid;
}
public void setMaximumCoordinatesPerLine(int maxCoordinatesPerLine)
{
if (maxCoordinatesPerLine <= 0) {
maxCoordinatesPerLine = 1;
return;
}
this.maxCoordinatesPerLine = maxCoordinatesPerLine;
}
public String write(Geometry geom)
{
StringBuffer buf = new StringBuffer();
write(geom, buf);
return buf.toString();
}
public void write(Geometry geometry, Writer writer)
throws IOException
{
writer.write(write(geometry));
}
/**
* Generates the GML representation of a JTS Geometry.
* @param g Geometry to output
*/
public void write(Geometry g, StringBuffer buf)
{
writeGeometry(g, attributeString(), 0, buf);
}
/**
* Generates the GML representation of a JTS Geometry.
* @param g Geometry to output
*/
private void writeGeometry(Geometry g, String attributes, int level, StringBuffer buf) {
/*
* order is important in this if-else list.
* E.g. homogeneous collections need to come before GeometryCollection
*/
if (g instanceof Point) {
writePoint((Point) g, attributes, level, buf);
} else if (g instanceof LinearRing) {
writeLinearRing((LinearRing) g, attributes, level, buf);
} else if (g instanceof LineString) {
writeLineString((LineString) g, attributes, level, buf);
} else if (g instanceof Polygon) {
writePolygon((Polygon) g, attributes, level, buf);
} else if (g instanceof MultiPoint) {
writeMultiPoint((MultiPoint) g, attributes, level, buf);
} else if (g instanceof MultiLineString) {
writeMultiLineString((MultiLineString) g, attributes, level, buf);
} else if (g instanceof MultiPolygon) {
writeMultiPolygon((MultiPolygon) g, attributes, level, buf);
} else if (g instanceof GeometryCollection) {
writeGeometryCollection((GeometryCollection) g, attributes, level, buf);
}
// throw an error for an unknown type?
}
private void startLine(StringBuffer buf, int level, String text)
{
if (linePrefix != null) buf.append(linePrefix);
buf.append(stringOfChar(' ', INDENT_SIZE * level));
buf.append(text);
}
private String geometryTag(String geometryName, String attributes)
{
StringBuffer buf = new StringBuffer();
buf.append("<gml:");
buf.append(geometryName);
if (attributes != null && attributes.length() > 0) {
buf.append(" ");
buf.append(attributes);
}
buf.append(">");
return buf.toString();
}
private String attributeString()
{
StringBuffer buf = new StringBuffer();
if (gid != null) {
buf.append(" gid='");
buf.append(gid);
buf.append("'");
}
if (srsName != null) {
buf.append(" srsName='");
buf.append(srsName);
buf.append("'");
}
return buf.toString();
}
//<gml:Point><gml:coordinates>1195156.78946687,382069.533723461</gml:coordinates></gml:Point>
private void writePoint(Point p, String attributes, int level, StringBuffer buf) {
startLine(buf, level, geometryTag("Point", attributes) + "\n");
write(new Coordinate[] { p.getCoordinate() }, level + 1, buf);
startLine(buf, level, "</gml:Point>\n");
}
//<gml:LineString><gml:coordinates>1195123.37289257,381985.763974674 1195120.22369473,381964.660533343 1195118.14929823,381942.597718511</gml:coordinates></gml:LineString>
private void writeLineString(LineString ls, String attributes, int level, StringBuffer buf) {
startLine(buf, level, geometryTag("LineString", attributes) + "\n");
write(ls.getCoordinates(), level + 1, buf);
startLine(buf, level, "</gml:LineString>\n");
}
//<gml:LinearRing><gml:coordinates>1226890.26761027,1466433.47430292 1226880.59239079,1466427.03208053...></coordinates></gml:LinearRing>
private void writeLinearRing(LinearRing lr, String attributes, int level, StringBuffer buf) {
startLine(buf, level, geometryTag("LinearRing", attributes) + "\n");
write(lr.getCoordinates(), level + 1, buf);
startLine(buf, level, "</gml:LinearRing>\n");
}
private void writePolygon(Polygon p, String attributes, int level, StringBuffer buf) {
startLine(buf, level, geometryTag("Polygon", attributes) + "\n");
startLine(buf, level, " <gml:outerBoundaryIs>\n");
writeLinearRing((LinearRing) p.getExteriorRing(), null, level + 1, buf);
startLine(buf, level, " </gml:outerBoundaryIs>\n");
for (int t = 0; t < p.getNumInteriorRing(); t++) {
startLine(buf, level, " <gml:innerBoundaryIs>\n");
writeLinearRing((LinearRing) p.getInteriorRingN(t), null, level + 1, buf);
startLine(buf, level, " </gml:innerBoundaryIs>\n");
}
startLine(buf, level, "</gml:Polygon>\n");
}
private void writeMultiPoint(MultiPoint mp, String attributes, int level, StringBuffer buf) {
startLine(buf, level, geometryTag("MultiPoint", attributes) + "\n");
for (int t = 0; t < mp.getNumGeometries(); t++) {
startLine(buf, level, " <gml:pointMember>\n");
writePoint((Point) mp.getGeometryN(t), null, level + 1, buf);
startLine(buf, level, " </gml:pointMember>\n");
}
startLine(buf, level, "</gml:MultiPoint>\n");
}
private void writeMultiLineString(MultiLineString mls, String attributes, int level, StringBuffer buf) {
startLine(buf, level, geometryTag("MultiLineString", attributes) + "\n");
for (int t = 0; t < mls.getNumGeometries(); t++) {
startLine(buf, level, " <gml:lineStringMember>\n");
writeLineString((LineString) mls.getGeometryN(t), null, level + 1, buf);
startLine(buf, level, " </gml:lineStringMember>\n");
}
startLine(buf, level, "</gml:MultiLineString>\n");
}
private void writeMultiPolygon(MultiPolygon mp, String attributes, int level, StringBuffer buf) {
startLine(buf, level, geometryTag("MultiPolygon", attributes) + "\n");
for (int t = 0; t < mp.getNumGeometries(); t++) {
startLine(buf, level, " <gml:polygonMember>\n");
writePolygon((Polygon) mp.getGeometryN(t), null, level + 1, buf);
startLine(buf, level, " </gml:polygonMember>\n");
}
startLine(buf, level, "</gml:MultiPolygon>\n");
}
private void writeGeometryCollection(GeometryCollection gc, String attributes, int level, StringBuffer buf) {
startLine(buf, level, geometryTag("MultiGeometry", attributes) + "\n");
for (int t = 0; t < gc.getNumGeometries(); t++) {
startLine(buf, level, " <gml:geometryMember>\n");
writeGeometry(gc.getGeometryN(t), null, level + 1, buf);
startLine(buf, level, " </gml:geometryMember>\n");
}
startLine(buf, level, "</gml:MultiGeometry>\n");
}
/**
* Takes a list of coordinates and converts it to GML.<br>
* 2d and 3d aware.
* Terminates the coordinate output with a newline.
*@param cs array of coordinates
*/
private void write(Coordinate[] coords, int level, StringBuffer buf) {
startLine(buf, level, "<gml:coordinates>");
int dim = 2;
if (coords.length > 0) {
if (!(Double.isNaN(coords[0].z)))
dim = 3;
}
boolean isNewLine = false;
for (int i = 0; i < coords.length; i++) {
if (isNewLine) {
startLine(buf, level, " ");
isNewLine = false;
}
if (dim == 2) {
buf.append(coords[i].x);
buf.append(coordinateSeparator);
buf.append(coords[i].y);
} else if (dim == 3) {
buf.append(coords[i].x);
buf.append(coordinateSeparator);
buf.append(coords[i].y);
buf.append(coordinateSeparator);
buf.append(coords[i].z);
}
buf.append(tupleSeparator);
// break output lines to prevent them from getting too long
if ((i + 1) % maxCoordinatesPerLine == 0 && i < coords.length - 1) {
buf.append("\n");
isNewLine = true;
}
}
buf.append("</gml:coordinates>\n");
}
}