/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2009, Open Source Geospatial Foundation (OSGeo)
* (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;
* version 2.1 of the License.
*
* 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.
*
* This is a port of the JTS WKTReader to handle SQL MM types such as Curve.
* We have subclassed so that our implementation can be used anywhere
* a WKTReader is needed. We would of tried for more code reuse except
* the base class has reduced everything to privatee methods.
*
* This class also contains code written by Mark Leslie for PostGIS while working
* at Refractions Reserach with whom we have a code contribution agreement.
*/
package org.geotools.geometry.jts;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.util.*;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static java.lang.Math.*;
/**
* Create a geometry from SQL Multi-Media Extension Well-Known Text which allows curves.
* <p>
*
*
*
* @source $URL$
* @version 1.7
* @see WKTWriter
*/
public class WKTReader2 extends WKTReader {
private static final String EMPTY = "EMPTY";
private static final String COMMA = ",";
private static final String L_PAREN = "(";
private static final String R_PAREN = ")";
private static final String NAN_SYMBOL = "NaN";
private static final double EPSILON_SQLMM = 1.0e-8;
private static final double M_PI = PI;
private static final double M_PI_2 = PI/2.0;
private GeometryFactory geometryFactory;
private PrecisionModel precisionModel;
private StreamTokenizer tokenizer;
/**
* Creates a reader that creates objects using the default {@link GeometryFactory}.
*/
public WKTReader2() {
this(new GeometryFactory());
}
/**
* Creates a reader that creates objects using the given {@link GeometryFactory}.
*
*@param geometryFactory
* the factory used to create <code>Geometry</code>s.
*/
public WKTReader2(GeometryFactory geometryFactory) {
this.geometryFactory = geometryFactory;
precisionModel = geometryFactory.getPrecisionModel();
}
/**
* Reads a Well-Known Text representation of a {@link Geometry} from a {@link String}.
*
* @param wellKnownText
* one or more <Geometry Tagged Text>strings (see the OpenGIS Simple Features
* Specification) separated by whitespace
* @return a <code>Geometry</code> specified by <code>wellKnownText</code>
* @throws ParseException
* if a parsing problem occurs
*/
public Geometry read(String wellKnownText) throws ParseException {
StringReader reader = new StringReader(wellKnownText);
try {
return read(reader);
} finally {
reader.close();
}
}
/**
* Reads a Well-Known Text representation of a {@link Geometry} from a {@link Reader}.
*
*@param reader
* a Reader which will return a <Geometry Tagged Text> string (see the OpenGIS Simple
* Features Specification)
*@return a <code>Geometry</code> read from <code>reader</code>
*@throws ParseException
* if a parsing problem occurs
*/
public Geometry read(Reader reader) throws ParseException {
tokenizer = new StreamTokenizer(reader);
// set tokenizer to NOT parse numbers
tokenizer.resetSyntax();
tokenizer.wordChars('a', 'z');
tokenizer.wordChars('A', 'Z');
tokenizer.wordChars(128 + 32, 255);
tokenizer.wordChars('0', '9');
tokenizer.wordChars('-', '-');
tokenizer.wordChars('+', '+');
tokenizer.wordChars('.', '.');
tokenizer.whitespaceChars(0, ' ');
tokenizer.commentChar('#');
try {
return readGeometryTaggedText();
} catch (IOException e) {
throw new ParseException(e.toString());
}
}
/**
* Returns the next array of <code>Coordinate</code>s in the stream.
*
*@return the next array of <code>Coordinate</code>s in the stream, or an empty array if EMPTY
* is the next element returned by the stream.
*@throws IOException
* if an I/O error occurs
*@throws ParseException
* if an unexpected token was encountered
*/
private Coordinate[] getCoordinates() throws IOException, ParseException {
String nextToken = getNextEmptyOrOpener();
if (nextToken.equals(EMPTY)) {
return new Coordinate[] {};
}
ArrayList coordinates = new ArrayList();
coordinates.add(getPreciseCoordinate());
nextToken = getNextCloserOrComma();
while (nextToken.equals(COMMA)) {
coordinates.add(getPreciseCoordinate());
nextToken = getNextCloserOrComma();
}
Coordinate[] array = new Coordinate[coordinates.size()];
return (Coordinate[]) coordinates.toArray(array);
}
private List<Coordinate> getCoordinateList(boolean openExpected) throws IOException, ParseException {
String nextToken;
if( openExpected ){
nextToken = getNextEmptyOrOpener();
if (nextToken.equals(EMPTY)) {
return Collections.emptyList();
}
}
ArrayList<Coordinate> coordinates = new ArrayList<Coordinate>();
coordinates.add(getPreciseCoordinate());
nextToken = getNextCloserOrComma();
while (nextToken.equals(COMMA)) {
coordinates.add(getPreciseCoordinate());
nextToken = getNextCloserOrComma();
}
Coordinate[] array = new Coordinate[coordinates.size()];
return coordinates;
}
private Coordinate getPreciseCoordinate() throws IOException, ParseException {
Coordinate coord = new Coordinate();
coord.x = getNextNumber();
coord.y = getNextNumber();
if (isNumberNext()) {
coord.z = getNextNumber();
}
precisionModel.makePrecise(coord);
return coord;
}
private boolean isNumberNext() throws IOException {
int type = tokenizer.nextToken();
tokenizer.pushBack();
return type == StreamTokenizer.TT_WORD;
}
/**
* Parses the next number in the stream. Numbers with exponents are handled. <tt>NaN</tt> values
* are handled correctly, and the case of the "NaN" token is not significant.
*
*@param tokenizer
* tokenizer over a stream of text in Well-known Text format. The next token must be
* a number.
*@return the next number in the stream
*@throws ParseException
* if the next token is not a valid number
*@throws IOException
* if an I/O error occurs
*/
private double getNextNumber() throws IOException, ParseException {
int type = tokenizer.nextToken();
switch (type) {
case StreamTokenizer.TT_WORD: {
if (tokenizer.sval.equalsIgnoreCase(NAN_SYMBOL)) {
return Double.NaN;
} else {
try {
return Double.parseDouble(tokenizer.sval);
} catch (NumberFormatException ex) {
throw new ParseException("Invalid number: " + tokenizer.sval);
}
}
}
}
parseError("number");
return 0.0;
}
/**
* Returns the next EMPTY or L_PAREN in the stream as uppercase text.
*
*@return the next EMPTY or L_PAREN in the stream as uppercase text.
*@throws ParseException
* if the next token is not EMPTY or L_PAREN
*@throws IOException
* if an I/O error occurs
*/
private String getNextEmptyOrOpener() throws IOException, ParseException {
String nextWord = getNextWord();
if (nextWord.equals(EMPTY) || nextWord.equals(L_PAREN)) {
return nextWord;
}
parseError(EMPTY + " or " + L_PAREN);
return null;
}
/**
* Returns the next R_PAREN or COMMA in the stream.
*
*@return the next R_PAREN or COMMA in the stream
*@throws ParseException
* if the next token is not R_PAREN or COMMA
*@throws IOException
* if an I/O error occurs
*/
private String getNextCloserOrComma() throws IOException, ParseException {
String nextWord = getNextWord();
if (nextWord.equals(COMMA) || nextWord.equals(R_PAREN)) {
return nextWord;
}
parseError(COMMA + " or " + R_PAREN);
return null;
}
/**
* Returns the next R_PAREN in the stream.
*
*@return the next R_PAREN in the stream
*@throws ParseException
* if the next token is not R_PAREN
*@throws IOException
* if an I/O error occurs
*/
private String getNextCloser() throws IOException, ParseException {
String nextWord = getNextWord();
if (nextWord.equals(R_PAREN)) {
return nextWord;
}
parseError(R_PAREN);
return null;
}
/**
* Returns the next word in the stream.
*
*@return the next word in the stream as uppercase text
*@throws ParseException
* if the next token is not a word
*@throws IOException
* if an I/O error occurs
*/
private String getNextWord() throws IOException, ParseException {
int type = tokenizer.nextToken();
switch (type) {
case StreamTokenizer.TT_WORD:
String word = tokenizer.sval;
if (word.equalsIgnoreCase(EMPTY))
return EMPTY;
return word;
case '(':
return L_PAREN;
case ')':
return R_PAREN;
case ',':
return COMMA;
}
parseError("word");
return null;
}
/**
* Throws a formatted ParseException for the current token.
*
* @param expected
* a description of what was expected
* @throws ParseException
* @throws AssertionFailedException
* if an invalid token is encountered
*/
private void parseError(String expected) throws ParseException {
// throws Asserts for tokens that should never be seen
if (tokenizer.ttype == StreamTokenizer.TT_NUMBER)
Assert.shouldNeverReachHere("Unexpected NUMBER token");
if (tokenizer.ttype == StreamTokenizer.TT_EOL)
Assert.shouldNeverReachHere("Unexpected EOL token");
String tokenStr = tokenString();
throw new ParseException("Expected " + expected + " but found " + tokenStr);
}
/**
* Gets a description of the current token
*
* @return a description of the current token
*/
private String tokenString() {
switch (tokenizer.ttype) {
case StreamTokenizer.TT_NUMBER:
return "<NUMBER>";
case StreamTokenizer.TT_EOL:
return "End-of-Line";
case StreamTokenizer.TT_EOF:
return "End-of-Stream";
case StreamTokenizer.TT_WORD:
return "'" + tokenizer.sval + "'";
}
return "'" + (char) tokenizer.ttype + "'";
}
/**
* Creates a <code>Geometry</code> using the next token in the stream.
*
*@return a <code>Geometry</code> specified by the next token in the stream
*@throws ParseException
* if the coordinates used to create a <code>Polygon</code> shell and holes do not
* form closed linestrings, or if an unexpected token was encountered
*@throws IOException
* if an I/O error occurs
*/
private Geometry readGeometryTaggedText() throws IOException, ParseException {
String type = null;
try {
type = getNextWord();
} catch (IOException e) {
return null;
} catch (ParseException e) {
return null;
}
if (type.equals("POINT")) {
return readPointText();
} else if (type.equalsIgnoreCase("LINESTRING")) {
return readLineStringText();
} else if (type.equalsIgnoreCase("LINEARRING")) {
return readLinearRingText();
} else if (type.equalsIgnoreCase("POLYGON")) {
return readPolygonText();
} else if (type.equalsIgnoreCase("MULTIPOINT")) {
return readMultiPointText();
} else if (type.equalsIgnoreCase("MULTILINESTRING")) {
return readMultiLineStringText();
} else if (type.equalsIgnoreCase("MULTIPOLYGON")) {
return readMultiPolygonText();
} else if (type.equalsIgnoreCase("GEOMETRYCOLLECTION")) {
return readGeometryCollectionText();
} else if (type.equalsIgnoreCase("CIRCULARSTRING")) {
return readCircularStringText();
} else if (type.equalsIgnoreCase("COMPOUNDCURVE")) {
return readCompoundCurveText();
} else if (type.equalsIgnoreCase("CURVEPOLYGON")) {
return readCurvePolygonText();
}
throw new ParseException("Unknown geometry type: " + type);
}
/**
* Creates a <code>Point</code> using the next token in the stream.
*
*@return a <code>Point</code> specified by the next token in the stream
*@throws IOException
* if an I/O error occurs
*@throws ParseException
* if an unexpected token was encountered
*/
private Point readPointText() throws IOException, ParseException {
String nextToken = getNextEmptyOrOpener();
if (nextToken.equals(EMPTY)) {
return geometryFactory.createPoint((Coordinate) null);
}
Point point = geometryFactory.createPoint(getPreciseCoordinate());
getNextCloser();
return point;
}
/**
* Creates a <code>LineString</code> using the next token in the stream.
*
*@return a <code>LineString</code> specified by the next token in the stream
*@throws IOException
* if an I/O error occurs
*@throws ParseException
* if an unexpected token was encountered
*/
private LineString readLineStringText() throws IOException, ParseException {
return geometryFactory.createLineString(getCoordinates());
}
/**
* Creates a <code>LineString</code> using the next token in the stream.
*
* @return
* @throws IOException
* @throws ParseException
*/
private LineString readCircularStringText() throws IOException, ParseException {
List<Coordinate> coordinates = getCoordinateList( true );
List<Coordinate> segmentized;
if (coordinates.size() < 3) {
segmentized = coordinates;
} else {
segmentized = new ArrayList<Coordinate>();
for (int i = 0; i < coordinates.size() - 1; i += 2) {
Coordinate p1 = coordinates.get(i);
Coordinate p2 = coordinates.get(i + 1);
Coordinate p3 = coordinates.get(i + 2);
List<Coordinate> segments = circularSegmentize(p1, p2, p3);
segmentized.addAll(segments.subList(0, segments.size() - 1));
}
segmentized.add(coordinates.get(coordinates.size() - 1));
}
// we can now process these coordinates based on the current precision model
Coordinate array[] = segmentized.toArray(new Coordinate[segmentized.size()]);
return geometryFactory.createLineString(array);
}
/**
* Constructs a series of segments based on the provided three points. The routine is based on
* the JTS Buffer code which devides a circle into 128 segments.
*
* @param p1
* @param p2
* @param p3
* @return List of Coordinates forming a set number of segments
*/
/*
private List<Coordinate> circularSegmentizeSimple(Coordinate p1, Coordinate p2, Coordinate p3) {
List<Coordinate> curve = new ArrayList<Coordinate>();
curve.add(p1);
curve.add(p2);
curve.add(p3);
return curve;
}
*/
private List<Coordinate> circularSegmentize(Coordinate p1, Coordinate p2, Coordinate p3) {
Coordinate center;
double centerX, centerY, radius;
double temp, bc, cd, determinate;
/* Closed circle */
if (abs(p1.x - p3.x) < EPSILON_SQLMM && abs(p1.y - p3.y) < EPSILON_SQLMM) {
centerX = p1.x + (p2.x - p1.x) / 2.0;
centerY = p1.y + (p2.y - p1.y) / 2.0;
center = new Coordinate();
center.x = centerX;
center.y = centerY;
radius = sqrt((centerX - p1.x) * (centerX - p1.x) + (centerY - p1.y) * (centerY - p1.y));
} else {
temp = p2.x * p2.x + p2.y * p2.y;
bc = (p1.x * p1.x + p1.y * p1.y - temp) / 2.0;
cd = (temp - p3.x * p3.x - p3.y * p3.y) / 2.0;
determinate = (p1.x - p2.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p2.y);
/* Check colinearity */
if (abs(determinate) < EPSILON_SQLMM) {
List<Coordinate> curve = new ArrayList<Coordinate>();
curve.add(p1);
// curve.add( p1 ); // forms a straight line not needed
curve.add(p3);
return curve;
}
determinate = 1.0 / determinate;
centerX = (bc * (p2.y - p3.y) - cd * (p1.y - p2.y)) * determinate;
centerY = ((p1.x - p2.x) * cd - (p2.x - p3.x) * bc) * determinate;
center = new Coordinate();
center.x = centerX;
center.y = centerY;
radius = sqrt((centerX - p1.x) * (centerX - p1.x) + (centerY - p1.y) * (centerY - p1.y));
}
return circularSegmentize( p1, p2, p3, center, radius, 32 );
}
private List<Coordinate> circularSegmentize(Coordinate p1, Coordinate p2, Coordinate p3, Coordinate center,
double radius, int perQuad) {
List<Coordinate> result;
Coordinate pbuf = new Coordinate();
int ptcount;
Coordinate pt;
double sweep = 0.0,
angle = 0.0,
increment = 0.0;
double a1, a2, a3, i;
if(radius < 0)
{
// does not form a circle
result = new ArrayList<Coordinate>();
result.add(p1);
result.add(p2);
return result;
}
a1 = atan2(p1.y - center.y, p1.x - center.x);
a2 = atan2(p2.y - center.y, p2.x - center.x);
a3 = atan2(p3.y - center.y, p3.x - center.x);
if(abs(p1.x - p3.x) < EPSILON_SQLMM
&& abs(p1.y - p3.y) < EPSILON_SQLMM)
{
sweep = 2*M_PI;
}
/* Clockwise */
else if(a1 > a2 && a2 > a3)
{
sweep = a3 - a1;
}
/* Counter-clockwise */
else if(a1 < a2 && a2 < a3)
{
sweep = a3 - a1;
}
/* Clockwise, wrap */
else if((a1 < a2 && a1 > a3) || (a2 < a3 && a1 > a3))
{
sweep = a3 - a1 + 2*M_PI;
}
/* Counter-clockwise, wrap */
else if((a1 > a2 && a1 < a3) || (a2 > a3 && a1 < a3))
{
sweep = a3 - a1 - 2*M_PI;
}
else
{
sweep = 0.0;
}
ptcount = (int) ceil(abs(perQuad * sweep / M_PI_2));
result = new ArrayList<Coordinate>( ptcount );
increment = M_PI_2 / perQuad;
if(sweep < 0) increment *= -1.0;
angle = a1;
result.add( p1 ); // start with first point
for(i = 0; i < ptcount - 1; i++)
{
pt = new Coordinate();
result.add( pt );
angle += increment;
if(increment > 0.0 && angle > M_PI) angle -= 2*M_PI;
if(increment < 0.0 && angle < -1*M_PI) angle -= 2*M_PI;
pt.x = center.x + radius*cos(angle);
pt.y = center.y + radius*sin(angle);
/*
//
// update this code to handle interopolation of z and m values
//
if((sweep > 0 && angle < a2) || (sweep < 0 && angle > a2))
{
if((sweep > 0 && a1 < a2) || (sweep < 0 && a1 > a2))
{
pbuf.z = interpolate_arc(angle, p1->z, a1, p2->z, a2);
pbuf.m = interpolate_arc(angle, p1->m, a1, p2->m, a2);
}
else
{
if(sweep > 0)
{
pbuf.z = interpolate_arc(angle, p1->z, a1-(2*M_PI), p2->z, a2);
pbuf.m = interpolate_arc(angle, p1->m, a1-(2*M_PI), p2->m, a2);
}
else
{
pbuf.z = interpolate_arc(angle, p1->z, a1+(2*M_PI), p2->z, a2);
pbuf.m = interpolate_arc(angle, p1->m, a1+(2*M_PI), p2->m, a2);
}
}
}
else
{
if((sweep > 0 && a2 < a3) || (sweep < 0 && a2 > a3))
{
pbuf.z = interpolate_arc(angle, p2->z, a2, p3->z, a3);
pbuf.m = interpolate_arc(angle, p2->m, a2, p3->m, a3);
}
else
{
if(sweep > 0)
{
pbuf.z = interpolate_arc(angle, p2->z, a2-(2*M_PI), p3->z, a3);
pbuf.m = interpolate_arc(angle, p2->m, a2-(2*M_PI), p3->m, a3);
}
else
{
pbuf.z = interpolate_arc(angle, p2->z, a2+(2*M_PI), p3->z, a3);
pbuf.m = interpolate_arc(angle, p2->m, a2+(2*M_PI), p3->m, a3);
}
}
}
*/
}
result.add( p3 );
return result;
}
private LineString readCompoundCurveText()
throws IOException, ParseException {
List<LineString> lineStrings = getLineStrings();
if( lineStrings.isEmpty() ){
// return an empty lineString?
return geometryFactory.createLineString(new Coordinate[] {});
}
if( lineStrings.size() == 1 ){
return lineStrings.get(0);
}
// we must gather these all into one - removing duplicates!
List<Coordinate> coords = new ArrayList<Coordinate>();
for( LineString segment : lineStrings ){
List<Coordinate> segmentCoordinates = Arrays.asList(segment.getCoordinates());
coords.addAll( segmentCoordinates.subList(0, segmentCoordinates.size()-1 ));
}
LineString last = lineStrings.get( lineStrings.size()-1);
Coordinate end = last.getCoordinateN( last.getNumPoints()-1);
coords.add( end );
return geometryFactory.createLineString( coords.toArray( new Coordinate[ coords.size()]));
}
/**
* Handles mixed line string notation - either LineString (the default) or CircularCurve.
* Isolated as a seperate method as I think we will need to call this from the polygon code.
*
* @return List of LineString (defined in a mixed format)
* @throws IOException
* @throws ParseException
*/
List<LineString> getLineStrings() throws IOException, ParseException {
ArrayList<LineString> lineStrings = new ArrayList<LineString>();
String nextWord = getNextEmptyOrOpener();
if (nextWord.equals(EMPTY)) {
return lineStrings;
}
// must be an opener!
nextWord = COMMA;
while( nextWord.equals( COMMA )){
nextWord = getNextWord();
if( nextWord.equals(L_PAREN) ){
List<Coordinate> coords = getCoordinateList(false);
LineString lineString = geometryFactory.createLineString( coords.toArray( new Coordinate[coords.size()]));
lineStrings.add(lineString);
}
else if( nextWord.equalsIgnoreCase("CIRCULARSTRING")){
LineString circularString = readCircularStringText();
lineStrings.add(circularString);
}
nextWord = getNextCloserOrComma();
}
return lineStrings;
}
/**
* This method will read a LineString, CircularString or CompoundCurve and return the result as a LinearRing.
* @return LinearRing
* <p>
* This method expects either "EMPTY", "(", "CIRCULARSTRING", or "COMPOIUNDCURVE" to start out with.
*
* @throws IOException
* @throws ParseException
*/
private LinearRing readCurvedLinearRingText() throws IOException, ParseException {
Coordinate ring[] = null;
String nextWord = getNextWord();
if( nextWord.equals(L_PAREN) ){
List<Coordinate> coords = getCoordinateList(false);
ring = coords.toArray( new Coordinate[coords.size()]);
}
else if( nextWord.equalsIgnoreCase("CIRCULARSTRING")){
LineString circularString = readCircularStringText();
ring = circularString.getCoordinates();
}
else if( nextWord.equalsIgnoreCase("COMPOUNDCURVE")){
LineString circularString = readCompoundCurveText();
ring = circularString.getCoordinates();
}
else {
parseError(L_PAREN + ", CIRCULARSTRING or COMPOUNDCURVE");
}
return geometryFactory.createLinearRing( ring );
}
/**
* Creates a <code>LinearRing</code> using the next token in the stream.
*
*@return a <code>LinearRing</code> specified by the next token in the stream
*@throws IOException
* if an I/O error occurs
*@throws ParseException
* if the coordinates used to create the <code>LinearRing</code> do not form a
* closed linestring, or if an unexpected token was encountered
*/
private LinearRing readLinearRingText() throws IOException, ParseException {
return geometryFactory.createLinearRing(getCoordinates());
}
/**
* Creates a <code>MultiPoint</code> using the next token in the stream.
*
*@return a <code>MultiPoint</code> specified by the next token in the stream
*@throws IOException
* if an I/O error occurs
*@throws ParseException
* if an unexpected token was encountered
*/
private MultiPoint readMultiPointText() throws IOException, ParseException {
return geometryFactory.createMultiPoint(toPoints(getCoordinates()));
}
/**
* Creates an array of <code>Point</code>s having the given <code>Coordinate</code> s.
*
*@param coordinates
* the <code>Coordinate</code>s with which to create the <code>Point</code>s
*@return <code>Point</code>s created using this <code>WKTReader</code> s
* <code>GeometryFactory</code>
*/
private Point[] toPoints(Coordinate[] coordinates) {
ArrayList points = new ArrayList();
for (int i = 0; i < coordinates.length; i++) {
points.add(geometryFactory.createPoint(coordinates[i]));
}
return (Point[]) points.toArray(new Point[] {});
}
/**
* Creates a <code>Polygon</code> using the next token in the stream.
*
*@return a <code>Polygon</code> specified by the next token in the stream
*@throws ParseException
* if the coordinates used to create the <code>Polygon</code> shell and holes do not
* form closed linestrings, or if an unexpected token was encountered.
*@throws IOException
* if an I/O error occurs
*/
private Polygon readPolygonText() throws IOException, ParseException {
String nextToken = getNextEmptyOrOpener();
if (nextToken.equals(EMPTY)) {
return geometryFactory.createPolygon(geometryFactory
.createLinearRing(new Coordinate[] {}), new LinearRing[] {});
}
ArrayList holes = new ArrayList();
LinearRing shell = readLinearRingText();
nextToken = getNextCloserOrComma();
while (nextToken.equals(COMMA)) {
LinearRing hole = readLinearRingText();
holes.add(hole);
nextToken = getNextCloserOrComma();
}
LinearRing[] array = new LinearRing[holes.size()];
return geometryFactory.createPolygon(shell, (LinearRing[]) holes.toArray(array));
}
private Polygon readCurvePolygonText() throws IOException, ParseException {
String nextToken = getNextEmptyOrOpener();
if (nextToken.equals(EMPTY)) {
return geometryFactory.createPolygon(geometryFactory
.createLinearRing(new Coordinate[] {}), new LinearRing[] {});
}
if( !nextToken.equals( L_PAREN )){
parseError("Ring expected");
}
LinearRing shell = readCurvedLinearRingText();
ArrayList holes = new ArrayList();
nextToken = getNextCloserOrComma();
while (nextToken.equals(COMMA)) {
LinearRing hole = readCurvedLinearRingText();
holes.add(hole);
nextToken = getNextCloserOrComma();
}
LinearRing[] array = new LinearRing[holes.size()];
return geometryFactory.createPolygon(shell, (LinearRing[]) holes.toArray(array));
}
/**
* Creates a <code>MultiLineString</code> using the next token in the stream.
*
*@return a <code>MultiLineString</code> specified by the next token in the stream
*@throws IOException
* if an I/O error occurs
*@throws ParseException
* if an unexpected token was encountered
*/
private com.vividsolutions.jts.geom.MultiLineString readMultiLineStringText()
throws IOException, ParseException {
String nextToken = getNextEmptyOrOpener();
if (nextToken.equals(EMPTY)) {
return geometryFactory.createMultiLineString(new LineString[] {});
}
ArrayList lineStrings = new ArrayList();
LineString lineString = readLineStringText();
lineStrings.add(lineString);
nextToken = getNextCloserOrComma();
while (nextToken.equals(COMMA)) {
lineString = readLineStringText();
lineStrings.add(lineString);
nextToken = getNextCloserOrComma();
}
LineString[] array = new LineString[lineStrings.size()];
return geometryFactory.createMultiLineString((LineString[]) lineStrings.toArray(array));
}
/**
* Creates a <code>MultiPolygon</code> using the next token in the stream.
*
*@return a <code>MultiPolygon</code> specified by the next token in the stream, or if if the
* coordinates used to create the <code>Polygon</code> shells and holes do not form
* closed linestrings.
*@throws IOException
* if an I/O error occurs
*@throws ParseException
* if an unexpected token was encountered
*/
private MultiPolygon readMultiPolygonText() throws IOException, ParseException {
String nextToken = getNextEmptyOrOpener();
if (nextToken.equals(EMPTY)) {
return geometryFactory.createMultiPolygon(new Polygon[] {});
}
ArrayList polygons = new ArrayList();
Polygon polygon = readPolygonText();
polygons.add(polygon);
nextToken = getNextCloserOrComma();
while (nextToken.equals(COMMA)) {
polygon = readPolygonText();
polygons.add(polygon);
nextToken = getNextCloserOrComma();
}
Polygon[] array = new Polygon[polygons.size()];
return geometryFactory.createMultiPolygon((Polygon[]) polygons.toArray(array));
}
/**
* Creates a <code>GeometryCollection</code> using the next token in the stream.
*
* @return a <code>GeometryCollection</code> specified by the next token in the stream
* @throws ParseException
* if the coordinates used to create a <code>Polygon</code> shell and holes do not
* form closed linestrings, or if an unexpected token was encountered
* @throws IOException
* if an I/O error occurs
*/
private GeometryCollection readGeometryCollectionText() throws IOException, ParseException {
String nextToken = getNextEmptyOrOpener();
if (nextToken.equals(EMPTY)) {
return geometryFactory.createGeometryCollection(new Geometry[] {});
}
ArrayList geometries = new ArrayList();
Geometry geometry = readGeometryTaggedText();
geometries.add(geometry);
nextToken = getNextCloserOrComma();
while (nextToken.equals(COMMA)) {
geometry = readGeometryTaggedText();
geometries.add(geometry);
nextToken = getNextCloserOrComma();
}
Geometry[] array = new Geometry[geometries.size()];
return geometryFactory.createGeometryCollection((Geometry[]) geometries.toArray(array));
}
}