WKTWriter.java

package org.cugos.wkg;

import java.util.ArrayList;
import java.util.List;

/**
 * Write a Geometry to a WKT String.
 * @author Jared Erickson
 */
public class WKTWriter implements Writer<String> {

    /**
     * The flag to determine is we should use inner parens for MultiPoint WKT.  The default is false.
     */
    private final boolean useInnerParensForMultiPoints;

    /**
     * Create a new WKTWriter
     */
    public WKTWriter() {
        this(false);
    }

    /**
     * Create a WKTWriter
     * @param useInnerParensForMultiPoints The flag to determine is we should use inner parens for MultiPoint WKT.
     */
    public WKTWriter(boolean useInnerParensForMultiPoints) {
        this.useInnerParensForMultiPoints = useInnerParensForMultiPoints;
    }

    /**
     * Write a Geometry to a WKT String
     * @param g The Geometry
     * @return A WKT String
     */
    @Override
    public String write(Geometry g) {
        return write(g, true, true);
    }

    @Override
    public String getName() {
        return "WKT";
    }

    /**
     * Write a Geometry to a WKT String
     * @param g The Geometry
     * @param includeSrid Whether to include the SRID prefix (SRID=4326;) or not
     * @param includeDimension Whether to include the dimension (M, Z, or ZM) before the Geometry type
     * @return A WKT String
     */
    public String write(Geometry g, boolean includeSrid, boolean includeDimension) {
        if (g instanceof Point) {
            return write((Point) g, includeSrid, includeDimension);
        } else if (g instanceof LineString) {
            return write((LineString) g, includeSrid, includeDimension);
        } else if (g instanceof LinearRing) {
            return write((LinearRing) g, includeSrid, includeDimension);
        } else if (g instanceof Triangle) {
            return write((Triangle) g, includeSrid, includeDimension);
        } else if (g instanceof Polygon) {
            return write((Polygon) g, includeSrid, includeDimension);
        } else if (g instanceof MultiPoint) {
            return write((MultiPoint) g, includeSrid, includeDimension);
        } else if (g instanceof MultiLineString) {
            return write((MultiLineString) g, includeSrid, includeDimension);
        } else if (g instanceof MultiPolygon) {
            return write((MultiPolygon) g, includeSrid, includeDimension);
        } else if (g instanceof GeometryCollection) {
            return write((GeometryCollection) g, includeSrid, includeDimension);
        } else if (g instanceof CircularString) {
            return write((CircularString) g, includeSrid, includeDimension);
        } else if (g instanceof CurvePolygon) {
            return write((CurvePolygon) g, includeSrid, includeDimension);
        } else if (g instanceof MultiCurve) {
            return write((MultiCurve) g, includeSrid, includeDimension);
        } else if (g instanceof PolyHedralSurface) {
            return write((PolyHedralSurface) g, includeSrid, includeDimension);
        } else if (g instanceof MultiSurface) {
            return write((MultiSurface) g, includeSrid, includeDimension);
        } else if (g instanceof CompoundCurve) {
            return write((CompoundCurve) g, includeSrid, includeDimension);
        } else if (g instanceof Tin) {
            return write((Tin) g, includeSrid, includeDimension);
        }
        else {
            throw new IllegalArgumentException("Unknown Geometry! " + g.getClass().getName());
        }
    }

    public String write(Point p) {
        return write(p, true, true);
    }

    public String write(Point p, boolean includeSrid, boolean includeDimension) {
        Coordinate coordinate = p.getCoordinate();
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(p, builder);
        }
        builder.append("POINT");
        if (coordinate.isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(p, builder);
            }
            builder.append(" (");
            addCoordinate(coordinate, builder);
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(LineString lineString) {
        return write(lineString, true, true);
    }

    public String write(LineString lineString, boolean includeSrid, boolean includeDimension) {
        List coordinates = lineString.getCoordinates();
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(lineString, builder);
        }
        builder.append("LINESTRING");
        if (coordinates.isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(lineString, builder);
            }
            builder.append(" (");
            addCoordinates(coordinates, builder);
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(LinearRing ring) {
        return write(ring, true, true);
    }

    public String write(LinearRing lineString, boolean includeSrid, boolean includeDimension) {
        List coordinates = lineString.getCoordinates();
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(lineString, builder);
        }
        builder.append("LINEARRING");
        if (coordinates.isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(lineString, builder);
            }
            builder.append(" (");
            addCoordinates(coordinates, builder);
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(MultiPoint mp) {
        return write(mp, true, true);
    }

    public String write(MultiPoint mp, boolean includeSrid, boolean includeDimension) {
        List<Point> points = mp.getPoints();
        List<Coordinate> coords = new ArrayList<Coordinate>();
        for(Point pt : points) {
            coords.add(pt.getCoordinate());
        }
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(mp, builder);
        }
        builder.append("MULTIPOINT");
        if (points.isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(mp, builder);
            }
            builder.append(" (");
            addCoordinates(coords, useInnerParensForMultiPoints, builder);
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(Polygon p) {
        return write(p, true, true);
    }

    public String write(Polygon p, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(p, builder);
        }
        builder.append("POLYGON");
        if (p.getOuterLinearRing() == null || p.getOuterLinearRing().getCoordinates().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(p, builder);
            }
            builder.append(" ((");
            // Outer ring
            addCoordinates(p.getOuterLinearRing().getCoordinates(), builder);
            builder.append(")");
            // Inner rings
            for (LinearRing ring : p.getInnerLinearRings()) {
                builder.append(", (");
                addCoordinates(ring.getCoordinates(), builder);
                builder.append(")");
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(MultiPolygon mp) {
        return write(mp, true, true);
    }

    public String write(MultiPolygon mp, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(mp, builder);
        }
        builder.append("MULTIPOLYGON");
        if (mp.getPolygons().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(mp, builder);
            }
            builder.append(" (");
            boolean firstPoly = true;
            for (Polygon polygon : mp.getPolygons()) {
                if (!firstPoly) {
                    builder.append(", ");
                } else {
                    firstPoly = false;
                }
                builder.append("((");
                // Outer ring
                addCoordinates(polygon.getOuterLinearRing().getCoordinates(), builder);
                builder.append(")");
                // Inner rings
                for (LinearRing ring : polygon.getInnerLinearRings()) {
                    builder.append(", (");
                    addCoordinates(ring.getCoordinates(), builder);
                    builder.append(")");
                }
                builder.append(")");
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(MultiLineString ml) {
        return write(ml, true, true);
    }

    public String write(MultiLineString ml, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(ml, builder);
        }
        builder.append("MULTILINESTRING");
        if (ml.getLineStrings().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(ml, builder);
            }
            builder.append(" (");
            boolean firstLine = true;
            for (LineString line : ml.getLineStrings()) {
                if (!firstLine) {
                    builder.append(", ");
                } else {
                    firstLine = false;
                }
                builder.append("(");
                addCoordinates(line.getCoordinates(), builder);
                builder.append(")");
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(GeometryCollection gc) {
        return write(gc, true, true);
    }

    public String write(GeometryCollection gc, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(gc, builder);
        }
        builder.append("GEOMETRYCOLLECTION");
        if (gc.getGeometries().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(gc, builder);
            }
            builder.append(" (");
            boolean first = true;
            for (Geometry g : gc.getGeometries()) {
                if (!first) {
                    builder.append(", ");
                } else {
                    first = false;
                }
                builder.append(write(g, false, false));
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(Triangle t) {
        return write(t, true, true);
    }

    public String write(Triangle t, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(t, builder);
        }
        builder.append("TRIANGLE");
        if (t.getOuterLinearRing().getCoordinates().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(t, builder);
            }
            builder.append(" ((");
            // Outer ring
            addCoordinates(t.getOuterLinearRing().getCoordinates(), builder);
            builder.append(")");
            // Inner rings
            for (LinearRing ring : t.getInnerLinearRings()) {
                builder.append(", (");
                addCoordinates(ring.getCoordinates(), builder);
                builder.append(")");
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(CircularString circularString) {
        return write(circularString, true, true);
    }

    public String write(CircularString circularString, boolean includeSrid, boolean includeDimension) {
        List coordinates = circularString.getCoordinates();
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(circularString, builder);
        }
        builder.append("CIRCULARSTRING");
        if (coordinates.isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(circularString, builder);
            }
            builder.append(" (");
            addCoordinates(coordinates, builder);
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(Tin tin) {
        return write(tin, true, true);
    }

    public String write(Tin tin, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(tin, builder);
        }
        builder.append("TIN");
        if (tin.getTriangles().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(tin, builder);
            }
            builder.append(" (");
            boolean first = true;
            for (Triangle t : tin.getTriangles()) {
                if (!first) {
                    builder.append(", ");
                } else {
                    first = false;
                }
                builder.append("((");
                // Outer ring
                addCoordinates(t.getOuterLinearRing().getCoordinates(), builder);
                builder.append(")");
                // Inner rings
                for (LinearRing ring : t.getInnerLinearRings()) {
                    builder.append(", (");
                    addCoordinates(ring.getCoordinates(), builder);
                    builder.append(")");
                }
                builder.append(")");
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(CompoundCurve compoundCurve) {
        return write(compoundCurve, true, true);
    }

    public String write(CompoundCurve compoundCurve, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(compoundCurve, builder);
        }
        builder.append("COMPOUNDCURVE");
        if (compoundCurve.getCurves().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(compoundCurve, builder);
            }
            builder.append(" (");
            boolean first = true;
            for (Curve curve : compoundCurve.getCurves()) {
                if (!first) {
                    builder.append(", ");
                } else {
                    first = false;
                }
                if (curve instanceof LineString) {
                    builder.append("(");
                    addCoordinates(((LineString) curve).getCoordinates(), builder);
                    builder.append(")");
                } else if (curve instanceof CircularString) {
                    builder.append(write((CircularString) curve, false, false));
                }
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(CurvePolygon p) {
        return write(p, true, true);
    }

    public String write(CurvePolygon p, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(p, builder);
        }
        builder.append("CURVEPOLYGON");
        if (p.getOuterCurve() == null || p.getOuterCurve().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(p, builder);
            }
            builder.append(" (");
            // Outer ring
            Curve outerCurve = p.getOuterCurve();
            if (outerCurve instanceof LineString) {
                LineString line = (LineString) outerCurve;
                builder.append("(");
                addCoordinates(line.getCoordinates(), builder);
                builder.append(")");
            } else if (outerCurve instanceof CircularString) {
                builder.append(write((CircularString) outerCurve, false, false));
            } else if (outerCurve instanceof CompoundCurve) {
                builder.append(write((CompoundCurve) outerCurve, false, false));
            }
            // Inner rings
            for (Curve curve : p.getInnerCurves()) {
                builder.append(", ");
                if (curve instanceof LineString) {
                    LineString line = (LineString) curve;
                    builder.append("(");
                    addCoordinates(line.getCoordinates(), builder);
                    builder.append(")");
                } else if (curve instanceof CircularString) {
                    builder.append(write((CircularString) curve, false, false));
                } else if (curve instanceof CompoundCurve) {
                    builder.append(write((CompoundCurve) curve, false, false));
                }
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(MultiCurve mc) {
        return write(mc, true, true);
    }

    public String write(MultiCurve mc, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(mc, builder);
        }
        builder.append("MULTICURVE");
        if (mc.isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(mc, builder);
            }
            builder.append(" (");
            boolean first = true;
            for (Curve c : mc.getCurves()) {
                if (first) {
                    first = false;
                } else {
                    builder.append(", ");
                }
                if (c instanceof LineString) {
                    LineString line = (LineString) c;
                    builder.append("(");
                    addCoordinates(line.getCoordinates(), builder);
                    builder.append(")");
                } else if (c instanceof CircularString) {
                    builder.append(write((CircularString) c, false, false));
                } else if (c instanceof CompoundCurve) {
                    builder.append(write((CompoundCurve) c, false, false));
                }
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(PolyHedralSurface phs) {
        return write(phs, true, true);
    }

    public String write(PolyHedralSurface phs, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(phs, builder);
        }
        builder.append("POLYHEDRALSURFACE");
        if (phs.getPolygons().isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(phs, builder);
            }
            builder.append(" (");
            boolean firstPoly = true;
            for (Polygon polygon : phs.getPolygons()) {
                if (!firstPoly) {
                    builder.append(", ");
                } else {
                    firstPoly = false;
                }
                builder.append("((");
                // Outer ring
                addCoordinates(polygon.getOuterLinearRing().getCoordinates(), builder);
                builder.append(")");
                // Inner rings
                for (LinearRing ring : polygon.getInnerLinearRings()) {
                    builder.append(", (");
                    addCoordinates(ring.getCoordinates(), builder);
                    builder.append(")");
                }
                builder.append(")");
            }
            builder.append(")");
        }
        return builder.toString();
    }

    public String write(MultiSurface ms) {
        return write(ms, true, true);
    }

    public String write(MultiSurface ms, boolean includeSrid, boolean includeDimension) {
        StringBuilder builder = new StringBuilder();
        if (includeSrid) {
            addSrid(ms, builder);
        }
        builder.append("MULTISURFACE");
        if (ms.isEmpty()) {
            builder.append(" EMPTY");
        } else {
            if (includeDimension) {
                addDimension(ms, builder);
            }
            builder.append(" (");
            boolean first = true;
            for (Surface s : ms.getSurfaces()) {
                if (first) {
                    first = false;
                } else {
                    builder.append(", ");
                }
                if (s instanceof Polygon) {
                    addPolygonText((Polygon) s, builder);
                } else if (s instanceof CurvePolygon) {
                    builder.append(write((CurvePolygon) s, false, false));
                }
            }
            builder.append(")");
        }
        return builder.toString();
    }

    protected void addCoordinates(List<Coordinate> coordinates, StringBuilder builder) {
        addCoordinates(coordinates, false, builder);
    }

    protected void addCoordinates(List<Coordinate> coordinates, boolean innerParens, StringBuilder builder) {
        boolean first = true;
        for (Coordinate coordinate : coordinates) {
            if (!first) {
                builder.append(", ");
            } else {
                first = false;
            }
            if (innerParens) {
                builder.append("(");
            }
            addCoordinate(coordinate, builder);
            if (innerParens) {
                builder.append(")");
            }
        }
    }

    /**
     * Add a Coordinate to the StringBuilder
     * @param coordinate The Coordinate
     * @param builder The StringBuilder
     */
    protected void addCoordinate(Coordinate coordinate, StringBuilder builder) {
        builder.append(coordinate.getX());
        builder.append(" ");
        builder.append(coordinate.getY());
        if (coordinate.getDimension() == Dimension.TwoMeasured) {
            builder.append(" ").append(coordinate.getM());
        } else if (coordinate.getDimension() == Dimension.Three) {
            builder.append(" ").append(coordinate.getZ());
        } else if (coordinate.getDimension() == Dimension.ThreeMeasured) {
            builder.append(" ").append(coordinate.getZ());
            builder.append(" ").append(coordinate.getM());
        }
    }

    /**
     * Add the SRID to the StringBuilder
     * @param g The Geometry
     * @param builder The StringBuilder
     */
    protected void addSrid(Geometry g, StringBuilder builder) {
        if (g.getSrid() != null) {
            builder.append("SRID=").append(g.getSrid()).append(";");
        }
    }

    /**
     * Add the Dimension (Z, M, or ZM) to the StringBuilder
     * @param g The Geometry
     * @param builder The StringBuilder
     */
    protected void addDimension(Geometry g, StringBuilder builder) {
        if (g.getDimension() == Dimension.TwoMeasured) {
            builder.append(" M");
        } else if (g.getDimension() == Dimension.Three) {
            builder.append(" Z");
        } else if (g.getDimension() == Dimension.ThreeMeasured) {
            builder.append(" ZM");
        }
    }

    /**
     * Add Polygon outer ring and inner rings to the StringBuilder
     * @param polygon The Polygon
     * @param builder The StringBuilder
     */
    protected void addPolygonText(Polygon polygon, StringBuilder builder) {
        builder.append("((");
        // Outer ring
        addCoordinates(polygon.getOuterLinearRing().getCoordinates(), builder);
        builder.append(")");
        // Inner rings
        for (LinearRing ring : polygon.getInnerLinearRings()) {
            builder.append(", (");
            addCoordinates(ring.getCoordinates(), builder);
            builder.append(")");
        }
        builder.append(")");
    }
}