|
|
|
svn_geotools
|
Author: bencaradocdavies
Date: 2009-10-25 00:31:14 -0400 (Sun, 25 Oct 2009) New Revision: 34231 Added: branches/geometry/spike/geometry/src/main/java/org/osgeo/geometry/WKTReader.java Log: Added WKT reader. (Still broken). Added: branches/geometry/spike/geometry/src/main/java/org/osgeo/geometry/WKTReader.java =================================================================== --- branches/geometry/spike/geometry/src/main/java/org/osgeo/geometry/WKTReader.java (rev 0) +++ branches/geometry/spike/geometry/src/main/java/org/osgeo/geometry/WKTReader.java 2009-10-25 04:31:14 UTC (rev 34231) @@ -0,0 +1,892 @@ +/* + * 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.osgeo.geometry; + +import static java.lang.Math.PI; +import static java.lang.Math.abs; +import static java.lang.Math.atan2; +import static java.lang.Math.ceil; +import static java.lang.Math.cos; +import static java.lang.Math.sin; +import static java.lang.Math.sqrt; + +import java.io.IOException; +import java.io.Reader; +import java.io.StreamTokenizer; +import java.io.StringReader; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.osgeo.commons.crs.CRS; +import org.osgeo.geometry.multi.MultiGeometry; +import org.osgeo.geometry.multi.MultiLineString; +import org.osgeo.geometry.multi.MultiPoint; +import org.osgeo.geometry.multi.MultiPolygon; +import org.osgeo.geometry.points.Points; +import org.osgeo.geometry.primitive.LineString; +import org.osgeo.geometry.primitive.LinearRing; +import org.osgeo.geometry.primitive.Point; +import org.osgeo.geometry.primitive.Polygon; + +/** + * Create a geometry from SQL Multi-Media Extension Well-Known Text which allows curves. + * + * @see WKTWriter + */ +public class 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 EasyGeometryBuilder geometryBuilder; + + private StreamTokenizer tokenizer; + + /** + * Creates a reader that creates objects using the default {@link GeometryFactory}. + */ + public WKTReader(GeometryFactory geometryFactory, CRS crs) { + this(new EasyGeometryBuilder(geometryFactory, crs)); + } + + /** + * Creates a reader that creates objects using the given {@link GeometryFactory}. + * + *@param geometryFactory + * the factory used to create <code>Geometry</code>s. + */ + private WKTReader(EasyGeometryBuilder geometryBuilder) { + this.geometryBuilder = geometryBuilder; + } + + /** + * 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 RuntimeException(e.toString()); + } + } + + /** + * Returns the next array of <code>Point</code>s in the stream. + * + *@return the next array of <code>Point</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 Points getPoints() throws IOException, ParseException { + String nextToken = getNextEmptyOrOpener(); + if (nextToken.equals(EMPTY)) { + return geometryBuilder.createPoints(new ArrayList<Point>()); + } + ArrayList<Point> coordinates = new ArrayList<Point>(); + coordinates.add(getPoint()); + nextToken = getNextCloserOrComma(); + while (nextToken.equals(COMMA)) { + coordinates.add(getPoint()); + nextToken = getNextCloserOrComma(); + } + return geometryBuilder.createPoints(coordinates); + } + + private List<Point> getPointList(boolean openExpected) throws IOException, ParseException { + String nextToken; + if (openExpected) { + nextToken = getNextEmptyOrOpener(); + if (nextToken.equals(EMPTY)) { + return Collections.emptyList(); + } + } + ArrayList<Point> coordinates = new ArrayList<Point>(); + coordinates.add(getPoint()); + nextToken = getNextCloserOrComma(); + while (nextToken.equals(COMMA)) { + coordinates.add(getPoint()); + nextToken = getNextCloserOrComma(); + } + return coordinates; + } + + private Point getPoint() throws ParseException, IOException { + List<Double> coordinates = new ArrayList<Double>(10); + while (isNumberNext()) { + coordinates.add(getNextNumber()); + } + if (coordinates.size() < 2) { + throw new ParseException( + "Not enough coordinates for point: expected at least two, but found + " + + coordinates.size(), coordinates.size()); + } + double[] coords = new double[coordinates.size()]; + for (int i = 0; i < coords.length; i++) { + coords[i] = coordinates.get(i); + } + return geometryBuilder.createPoint(null, coords); + } + + 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, -1); + } + } + } + } + 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) { + throw new RuntimeException("Unexpected NUMBER token"); + } + if (tokenizer.ttype == StreamTokenizer.TT_EOL) { + throw new RuntimeException("Unexpected EOL token"); + } + + String tokenStr = tokenString(); + throw new ParseException("Expected " + expected + " but found " + tokenStr, -1); + } + + /** + * 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 readMultiGeometryText(); + } 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, -1); + } + + /** + * 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 null; + } + Point point = geometryBuilder.createPoint(null, getPoint().getAsArray()); + 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 geometryBuilder.createLineString(null, getPoints()); + } + + /** + * Creates a <code>LineString</code> using the next token in the stream. + * + * @return + * @throws IOException + * @throws ParseException + */ + private LineString readCircularStringText() throws IOException, ParseException { + List<Point> coordinates = getPointList(true); + List<Point> segmentized; + if (coordinates.size() < 3) { + segmentized = coordinates; + } else { + segmentized = new ArrayList<Point>(); + for (int i = 0; i < coordinates.size() - 1; i += 2) { + Point p1 = coordinates.get(i); + Point p2 = coordinates.get(i + 1); + Point p3 = coordinates.get(i + 2); + + List<Point> segments = circularSegmentize(p1, p2, p3); + segmentized.addAll(segments.subList(0, segments.size() - 1)); + } + segmentized.add(coordinates.get(coordinates.size() - 1)); + } + return geometryBuilder.createLineString(null, geometryBuilder.createPoints(segmentized)); + + } + + /** + * 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 Points forming a set number of segments + */ + /* + * private List<Point> circularSegmentizeSimple(Point p1, Point p2, Point p3) { List<Point> + * curve = new ArrayList<Point>(); curve.add(p1); curve.add(p2); curve.add(p3); + * + * return curve; } + */ + + private List<Point> circularSegmentize(Point p1, Point p2, Point p3) { + Point center; + + double centerX, centerY, radius; + double temp, bc, cd, determinate; + + /* Closed circle */ + if (abs(p1.get0() - p3.get0()) < EPSILON_SQLMM + && abs(p1.get1() - p3.get1()) < EPSILON_SQLMM) { + centerX = p1.get0() + (p2.get0() - p1.get0()) / 2.0; + centerY = p1.get1() + (p2.get1() - p1.get1()) / 2.0; + center = geometryBuilder.createPoint(null, centerX, centerY); + + radius = sqrt((centerX - p1.get0()) * (centerX - p1.get0()) + (centerY - p1.get1()) + * (centerY - p1.get1())); + } else { + temp = p2.get0() * p2.get0() + p2.get1() * p2.get1(); + bc = (p1.get0() * p1.get0() + p1.get1() * p1.get1() - temp) / 2.0; + cd = (temp - p3.get0() * p3.get0() - p3.get1() * p3.get1()) / 2.0; + determinate = (p1.get0() - p2.get0()) * (p2.get1() - p3.get1()) + - (p2.get0() - p3.get0()) * (p1.get1() - p2.get1()); + + /* Check colinearity */ + if (abs(determinate) < EPSILON_SQLMM) { + List<Point> curve = new ArrayList<Point>(); + curve.add(p1); + // curve.add( p1 ); // forms a straight line not needed + curve.add(p3); + + return curve; + } + determinate = 1.0 / determinate; + centerX = (bc * (p2.get1() - p3.get1()) - cd * (p1.get1() - p2.get1())) * determinate; + centerY = ((p1.get0() - p2.get0()) * cd - (p2.get0() - p3.get0()) * bc) * determinate; + center = geometryBuilder.createPoint(null, centerX, centerY); + radius = sqrt((centerX - p1.get0()) * (centerX - p1.get0()) + (centerY - p1.get1()) + * (centerY - p1.get1())); + } + return circularSegmentize(p1, p2, p3, center, radius, 32); + } + + private List<Point> circularSegmentize(Point p1, Point p2, Point p3, Point center, + double radius, int perQuad) { + List<Point> result; + + int ptcount; + + 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<Point>(); + result.add(p1); + result.add(p2); + return result; + } + + a1 = atan2(p1.get1() - center.get1(), p1.get0() - center.get0()); + a2 = atan2(p2.get1() - center.get1(), p2.get0() - center.get0()); + a3 = atan2(p3.get1() - center.get1(), p3.get0() - center.get0()); + + if (abs(p1.get0() - p3.get0()) < EPSILON_SQLMM + && abs(p1.get1() - p3.get1()) < 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<Point>(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++) { + + 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; + + double ptx = center.get0() + radius * cos(angle); + double pty = center.get1() + radius * sin(angle); + + result.add(geometryBuilder.createPoint(null, ptx, pty)); + + /* + * // // 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 geometryBuilder.createLineString(null, geometryBuilder + .createPoints(new ArrayList<Point>())); + } + if (lineStrings.size() == 1) { + return lineStrings.get(0); + } + // we must gather these all into one - removing duplicates! + List<Point> coords = new ArrayList<Point>(); + for (LineString segment : lineStrings) { + List<Point> segmentPoints = Arrays.asList(segment.getPoints()); + coords.addAll(segmentPoints.subList(0, segmentPoints.size() - 1)); + } + LineString last = lineStrings.get(lineStrings.size() - 1); + Point end = last.getPointN(last.getNumPoints() - 1); + coords.add(end); + + return geometryBuilder.createLineString(coords.toArray(new Point[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<Point> coords = getPointList(false); + LineString lineString = geometryBuilder.createLineString(coords + .toArray(new Point[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 { + Point ring[] = null; + + String nextWord = getNextWord(); + if (nextWord.equals(L_PAREN)) { + List<Point> coords = getPointList(false); + ring = coords.toArray(new Point[coords.size()]); + } else if (nextWord.equalsIgnoreCase("CIRCULARSTRING")) { + LineString circularString = readCircularStringText(); + ring = circularString.getPoints(); + } else if (nextWord.equalsIgnoreCase("COMPOUNDCURVE")) { + LineString circularString = readCompoundCurveText(); + ring = circularString.getPoints(); + } else { + parseError(L_PAREN + ", CIRCULARSTRING or COMPOUNDCURVE"); + } + return geometryBuilder.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 geometryBuilder.createLinearRing(getPoints()); + } + + /** + * 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 geometryBuilder.createMultiPoint(toPoints(getPoints())); + } + + /** + * Creates an array of <code>Point</code>s having the given <code>Point</code> s. + * + *@param coordinates + * the <code>Point</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(Point[] coordinates) { + ArrayList points = new ArrayList(); + for (int i = 0; i < coordinates.length; i++) { + points.add(geometryBuilder.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 geometryBuilder.createPolygon(geometryBuilder.createLinearRing(new Point[] {}), + 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 geometryBuilder.createPolygon(shell, (LinearRing[]) holes.toArray(array)); + } + + private Polygon readCurvePolygonText() throws IOException, ParseException { + String nextToken = getNextEmptyOrOpener(); + if (nextToken.equals(EMPTY)) { + return geometryBuilder.createPolygon(geometryBuilder.createLinearRing(new Point[] {}), + 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 geometryBuilder.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 MultiLineString readMultiLineStringText() throws IOException, ParseException { + String nextToken = getNextEmptyOrOpener(); + if (nextToken.equals(EMPTY)) { + return geometryBuilder.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 geometryBuilder.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 geometryBuilder.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 geometryBuilder.createMultiPolygon((Polygon[]) polygons.toArray(array)); + } + + /** + * Creates a <code>MultiGeometry</code> using the next token in the stream. + * + * @return a <code>MultiGeometry</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 MultiGeometry readMultiGeometryText() throws IOException, ParseException { + String nextToken = getNextEmptyOrOpener(); + if (nextToken.equals(EMPTY)) { + return geometryBuilder.createMultiGeometry(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 geometryBuilder.createMultiGeometry((Geometry[]) geometries.toArray(array)); + } + +} Property changes on: branches/geometry/spike/geometry/src/main/java/org/osgeo/geometry/WKTReader.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Id URL Added: svn:eol-style + native ------------------------------------------------------------------------------ Come build with us! The BlackBerry(R) Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9 - 12, 2009. Register now! http://p.sf.net/sfu/devconference _______________________________________________ GeoTools-commits mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/geotools-commits |
||||||||||||||||
| Free Embeddable Forum Powered by Nabble | Help |