/* * The JTS Topology Suite is a collection of Java classes that * implement the fundamental operations required to validate a given * geo-spatial data set to a known topological specification. * * Copyright (C) 2001 Vivid Solutions * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; 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 org.teiid.olingo.common; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble; import org.teiid.core.TeiidRuntimeException; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.io.WKTReader; import com.vividsolutions.jts.util.Assert; /** * Writes the Well-Known Text representation of a {@link Geometry}. * The Well-Known Text format is defined in the * OGC <A HREF="http://www.opengis.org/techno/specs.htm"> * <i>Simple Features Specification for SQL</i></A>. * See {@link WKTReader} for a formal specification of the format syntax. * <p> * The <code>WKTWriter</code> outputs coordinates rounded to the precision * model. Only the maximum number of decimal places * necessary to represent the ordinates to the required precision will be * output. * <p> * The SFS WKT spec does not define a special tag for {@link LinearRing}s. * Under the spec, rings are output as <code>LINESTRING</code>s. * In order to allow precisely specifying constructed geometries, * JTS also supports a non-standard <code>LINEARRING</code> tag which is used * to output LinearRings. * * Forked from JTS to conform to OData BNF * */ class ODataWKTWriter { private int outputDimension = 2; private boolean isFormatted = false; /** * Creates a new WKTWriter with default settings */ public ODataWKTWriter() { } /** * Creates a writer that writes {@link Geometry}s with * the given output dimension (2 or 3). * If the specified output dimension is 3, the Z value * of coordinates will be written if it is present * (i.e. if it is not <code>Double.NaN</code>). * * @param outputDimension the coordinate dimension to output (2 or 3) */ public ODataWKTWriter(int outputDimension) { this.outputDimension = outputDimension; if (outputDimension < 2 || outputDimension > 3) throw new IllegalArgumentException("Invalid output dimension (must be 2 or 3)"); } /** * Converts a <code>Geometry</code> to its Well-known Text representation. * *@param geometry a <code>Geometry</code> to process *@return a <Geometry Tagged Text> string (see the OpenGIS Simple * Features Specification) */ public String write(Geometry geometry) { Writer sw = new StringWriter(); try { writeFormatted(geometry, isFormatted, sw); } catch (IOException ex) { Assert.shouldNeverReachHere(); } return sw.toString(); } /** * Converts a <code>Geometry</code> to its Well-known Text representation. * *@param geometry a <code>Geometry</code> to process */ public void write(Geometry geometry, Writer writer) throws IOException { writeFormatted(geometry, false, writer); } /** * Same as <code>write</code>, but with newlines and spaces to make the * well-known text more readable. * *@param geometry a <code>Geometry</code> to process *@return a <Geometry Tagged Text> string (see the OpenGIS Simple * Features Specification), with newlines and spaces */ public String writeFormatted(Geometry geometry) { Writer sw = new StringWriter(); try { writeFormatted(geometry, true, sw); } catch (IOException ex) { Assert.shouldNeverReachHere(); } return sw.toString(); } /** * Same as <code>write</code>, but with newlines and spaces to make the * well-known text more readable. * *@param geometry a <code>Geometry</code> to process */ public void writeFormatted(Geometry geometry, Writer writer) throws IOException { writeFormatted(geometry, true, writer); } /** * Converts a <code>Geometry</code> to its Well-known Text representation. * *@param geometry a <code>Geometry</code> to process */ private void writeFormatted(Geometry geometry, boolean useFormatting, Writer writer) throws IOException { appendGeometryTaggedText(geometry, 0, writer); } /** * Converts a <code>Geometry</code> to <Geometry Tagged Text> format, * then appends it to the writer. * *@param geometry the <code>Geometry</code> to process *@param writer the output writer to append to */ private void appendGeometryTaggedText(Geometry geometry, int level, Writer writer) throws IOException { if (geometry instanceof Point) { Point point = (Point) geometry; appendPointTaggedText(point.getCoordinate(), level, writer, point.getPrecisionModel()); } else if (geometry instanceof LineString) { appendLineStringTaggedText((LineString) geometry, level, writer); } else if (geometry instanceof Polygon) { appendPolygonTaggedText((Polygon) geometry, level, writer); } else if (geometry instanceof MultiPoint) { appendMultiPointTaggedText((MultiPoint) geometry, level, writer); } else if (geometry instanceof MultiLineString) { appendMultiLineStringTaggedText((MultiLineString) geometry, level, writer); } else if (geometry instanceof MultiPolygon) { appendMultiPolygonTaggedText((MultiPolygon) geometry, level, writer); } else if (geometry instanceof GeometryCollection) { appendGeometryCollectionTaggedText((GeometryCollection) geometry, level, writer); } else { Assert.shouldNeverReachHere("Unsupported Geometry implementation:" + geometry.getClass()); } } /** * Converts a <code>Coordinate</code> to <Point Tagged Text> format, * then appends it to the writer. * *@param coordinate the <code>Coordinate</code> to process *@param writer the output writer to append to *@param precisionModel the <code>PrecisionModel</code> to use to convert * from a precise coordinate to an external coordinate */ private void appendPointTaggedText(Coordinate coordinate, int level, Writer writer, PrecisionModel precisionModel) throws IOException { writer.write("Point"); appendPointText(coordinate, level, writer, precisionModel); } /** * Converts a <code>LineString</code> to <LineString Tagged Text> * format, then appends it to the writer. * *@param lineString the <code>LineString</code> to process *@param writer the output writer to append to */ private void appendLineStringTaggedText(LineString lineString, int level, Writer writer) throws IOException { writer.write("LineString"); appendLineStringText(lineString, level, false, writer); } /** * Converts a <code>Polygon</code> to <Polygon Tagged Text> format, * then appends it to the writer. * *@param polygon the <code>Polygon</code> to process *@param writer the output writer to append to */ private void appendPolygonTaggedText(Polygon polygon, int level, Writer writer) throws IOException { writer.write("Polygon"); appendPolygonText(polygon, level, false, writer); } /** * Converts a <code>MultiPoint</code> to <MultiPoint Tagged Text> * format, then appends it to the writer. * *@param multipoint the <code>MultiPoint</code> to process *@param writer the output writer to append to */ private void appendMultiPointTaggedText(MultiPoint multipoint, int level, Writer writer) throws IOException { writer.write("MultiPoint"); appendMultiPointText(multipoint, level, writer); } /** * Converts a <code>MultiLineString</code> to <MultiLineString Tagged * Text> format, then appends it to the writer. * *@param multiLineString the <code>MultiLineString</code> to process *@param writer the output writer to append to */ private void appendMultiLineStringTaggedText(MultiLineString multiLineString, int level, Writer writer) throws IOException { writer.write("MultiLineString"); appendMultiLineStringText(multiLineString, level, false, writer); } /** * Converts a <code>MultiPolygon</code> to <MultiPolygon Tagged Text> * format, then appends it to the writer. * *@param multiPolygon the <code>MultiPolygon</code> to process *@param writer the output writer to append to */ private void appendMultiPolygonTaggedText(MultiPolygon multiPolygon, int level, Writer writer) throws IOException { writer.write("MultiPolygon"); appendMultiPolygonText(multiPolygon, level, writer); } /** * Converts a <code>GeometryCollection</code> to <GeometryCollection * Tagged Text> format, then appends it to the writer. * *@param geometryCollection the <code>GeometryCollection</code> to process *@param writer the output writer to append to */ private void appendGeometryCollectionTaggedText(GeometryCollection geometryCollection, int level, Writer writer) throws IOException { writer.write("Collection"); appendGeometryCollectionText(geometryCollection, level, writer); } /** * Converts a <code>Coordinate</code> to <Point Text> format, then * appends it to the writer. * *@param coordinate the <code>Coordinate</code> to process *@param writer the output writer to append to *@param precisionModel the <code>PrecisionModel</code> to use to convert * from a precise coordinate to an external coordinate */ private void appendPointText(Coordinate coordinate, int level, Writer writer, PrecisionModel precisionModel) throws IOException { writer.write("("); if (coordinate != null) { appendCoordinate(coordinate, writer); } writer.write(")"); } /** * Appends the i'th coordinate from the sequence to the writer * * @param seq the <code>CoordinateSequence</code> to process * @param i the index of the coordinate to write * @param writer the output writer to append to */ private void appendCoordinate(CoordinateSequence seq, int i, Writer writer) throws IOException { writer.write(writeNumber(seq.getX(i)) + " " + writeNumber(seq.getY(i))); if (outputDimension >= 3 && seq.getDimension() >= 3) { double z = seq.getOrdinate(i, 3); if (! Double.isNaN(z)) { writer.write(" "); writer.write(writeNumber(z)); } } } /** * Converts a <code>Coordinate</code> to <code><Point></code> format, * then appends it to the writer. * *@param coordinate the <code>Coordinate</code> to process *@param writer the output writer to append to */ private void appendCoordinate(Coordinate coordinate, Writer writer) throws IOException { writer.write(writeNumber(coordinate.x) + " " + writeNumber(coordinate.y)); if (outputDimension >= 3 && ! Double.isNaN(coordinate.z)) { writer.write(" "); writer.write(writeNumber(coordinate.z)); } } /** * Converts a <code>double</code> to a <code>String</code>, not in scientific * notation. * *@param d the <code>double</code> to convert *@return the <code>double</code> as a <code>String</code>, not in * scientific notation */ private String writeNumber(double d) { try { return EdmDouble.getInstance().valueToString(d, false, null, null, null, false); } catch (EdmPrimitiveTypeException e) { throw new TeiidRuntimeException(e); } } /** * Converts a <code>LineString</code> to <LineString Text> format, then * appends it to the writer. * *@param lineString the <code>LineString</code> to process *@param writer the output writer to append to */ private void appendLineStringText(LineString lineString, int level, boolean doIndent, Writer writer) throws IOException { writer.write("("); for (int i = 0; i < lineString.getNumPoints(); i++) { if (i > 0) { writer.write(", "); } appendCoordinate(lineString.getCoordinateN(i), writer); } writer.write(")"); } /** * Converts a <code>Polygon</code> to <Polygon Text> format, then * appends it to the writer. * *@param polygon the <code>Polygon</code> to process *@param writer the output writer to append to */ private void appendPolygonText(Polygon polygon, int level, boolean indentFirst, Writer writer) throws IOException { writer.write("("); appendLineStringText(polygon.getExteriorRing(), level, false, writer); for (int i = 0; i < polygon.getNumInteriorRing(); i++) { writer.write(", "); appendLineStringText(polygon.getInteriorRingN(i), level + 1, true, writer); } writer.write(")"); } /** * Converts a <code>MultiPoint</code> to <MultiPoint Text> format, then * appends it to the writer. * *@param multiPoint the <code>MultiPoint</code> to process *@param writer the output writer to append to */ private void appendMultiPointText(MultiPoint multiPoint, int level, Writer writer) throws IOException { writer.write("("); for (int i = 0; i < multiPoint.getNumGeometries(); i++) { if (i > 0) { writer.write(", "); } writer.write("("); appendCoordinate(((Point) multiPoint.getGeometryN(i)).getCoordinate(), writer); writer.write(")"); } writer.write(")"); } /** * Converts a <code>MultiLineString</code> to <MultiLineString Text> * format, then appends it to the writer. * *@param multiLineString the <code>MultiLineString</code> to process *@param writer the output writer to append to */ private void appendMultiLineStringText(MultiLineString multiLineString, int level, boolean indentFirst, Writer writer) throws IOException { int level2 = level; boolean doIndent = indentFirst; writer.write("("); for (int i = 0; i < multiLineString.getNumGeometries(); i++) { if (i > 0) { writer.write(", "); level2 = level + 1; doIndent = true; } appendLineStringText((LineString) multiLineString.getGeometryN(i), level2, doIndent, writer); } writer.write(")"); } /** * Converts a <code>MultiPolygon</code> to <MultiPolygon Text> format, * then appends it to the writer. * *@param multiPolygon the <code>MultiPolygon</code> to process *@param writer the output writer to append to */ private void appendMultiPolygonText(MultiPolygon multiPolygon, int level, Writer writer) throws IOException { int level2 = level; boolean doIndent = false; writer.write("("); for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { if (i > 0) { writer.write(", "); level2 = level + 1; doIndent = true; } appendPolygonText((Polygon) multiPolygon.getGeometryN(i), level2, doIndent, writer); } writer.write(")"); } /** * Converts a <code>GeometryCollection</code> to <GeometryCollectionText> * format, then appends it to the writer. * *@param geometryCollection the <code>GeometryCollection</code> to process *@param writer the output writer to append to */ private void appendGeometryCollectionText(GeometryCollection geometryCollection, int level, Writer writer) throws IOException { int level2 = level; writer.write("("); for (int i = 0; i < geometryCollection.getNumGeometries(); i++) { if (i > 0) { writer.write(", "); level2 = level + 1; } appendGeometryTaggedText(geometryCollection.getGeometryN(i), level2, writer); } writer.write(")"); } }