svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.geometry / org.gvsig.fmap.geometry.jts / src / main / java / org / gvsig / fmap / geom / jts / operation / fromwkt / EWKTParser.java @ 47432
History | View | Annotate | Download (41.9 KB)
1 | 42875 | fdiaz | /*
|
---|---|---|---|
2 | * $Id$
|
||
3 | *
|
||
4 | * This file is an adapted version of the JTS WKTReader. It has
|
||
5 | * been extended by Martin Steinwender to deal with Measured coordinates.
|
||
6 | *
|
||
7 | * Original copyright notice:
|
||
8 | *
|
||
9 | * The JTS Topology Suite is a collection of Java classes that
|
||
10 | * implement the fundamental operations required to validate a given
|
||
11 | * geo-spatial data set to a known topological specification.
|
||
12 | *
|
||
13 | * Copyright (C) 2001 Vivid Solutions
|
||
14 | *
|
||
15 | * This library is free software; you can redistribute it and/or
|
||
16 | * modify it under the terms of the GNU Lesser General Public
|
||
17 | * License as published by the Free Software Foundation; either
|
||
18 | * version 2.1 of the License, or (at your option) any later version.
|
||
19 | *
|
||
20 | * This library is distributed in the hope that it will be useful,
|
||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
23 | * Lesser General Public License for more details.
|
||
24 | *
|
||
25 | * You should have received a copy of the GNU Lesser General Public
|
||
26 | * License along with this library; if not, write to the Free Software
|
||
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
28 | *
|
||
29 | * For more information, contact:
|
||
30 | *
|
||
31 | * Vivid Solutions
|
||
32 | * Suite #1A
|
||
33 | * 2328 Government Street
|
||
34 | * Victoria BC V8T 5G5
|
||
35 | * Canada
|
||
36 | *
|
||
37 | * (250)385-6040
|
||
38 | * www.vividsolutions.com
|
||
39 | */
|
||
40 | |||
41 | |||
42 | package org.gvsig.fmap.geom.jts.operation.fromwkt; |
||
43 | |||
44 | import com.vividsolutions.jts.geom.Coordinate; |
||
45 | 47432 | fdiaz | import com.vividsolutions.jts.geom.CoordinateSequence; |
46 | 42875 | fdiaz | import com.vividsolutions.jts.geom.GeometryFactory; |
47 | import com.vividsolutions.jts.geom.PrecisionModel; |
||
48 | import com.vividsolutions.jts.io.ParseException; |
||
49 | import com.vividsolutions.jts.io.WKTWriter; |
||
50 | import com.vividsolutions.jts.util.Assert; |
||
51 | 46331 | fdiaz | import java.io.IOException; |
52 | import java.io.Reader; |
||
53 | import java.io.StreamTokenizer; |
||
54 | import java.io.StringReader; |
||
55 | import java.util.ArrayList; |
||
56 | 47432 | fdiaz | import org.apache.commons.lang3.StringUtils; |
57 | 42875 | fdiaz | import org.gvsig.fmap.geom.Geometry; |
58 | import org.gvsig.fmap.geom.Geometry.SUBTYPES; |
||
59 | import org.gvsig.fmap.geom.Geometry.TYPES; |
||
60 | import org.gvsig.fmap.geom.GeometryLocator; |
||
61 | import org.gvsig.fmap.geom.GeometryManager; |
||
62 | import org.gvsig.fmap.geom.aggregate.MultiLine; |
||
63 | import org.gvsig.fmap.geom.aggregate.MultiPoint; |
||
64 | import org.gvsig.fmap.geom.aggregate.MultiPolygon; |
||
65 | import org.gvsig.fmap.geom.exception.CreateGeometryException; |
||
66 | 46331 | fdiaz | import org.gvsig.fmap.geom.jts.mgeom.MCoordinate; |
67 | import org.gvsig.fmap.geom.jts.mgeom.MGeometryFactory; |
||
68 | 42875 | fdiaz | import org.gvsig.fmap.geom.primitive.Line; |
69 | import org.gvsig.fmap.geom.primitive.Point; |
||
70 | import org.gvsig.fmap.geom.primitive.Polygon; |
||
71 | import org.gvsig.fmap.geom.primitive.Ring; |
||
72 | import org.gvsig.tools.locator.LocatorException; |
||
73 | |||
74 | /**
|
||
75 | * Converts a geometry in EWKT to a JTS-Geometry.
|
||
76 | * <p/>
|
||
77 | * <code>EWKTReader</code> supports
|
||
78 | * extracting <code>Geometry</code> objects from either {@link java.io.Reader}s or
|
||
79 | * {@link String}s. This allows it to function as a parser to read <code>Geometry</code>
|
||
80 | * objects from text blocks embedded in other data formats (e.g. XML). <P>
|
||
81 | * <p/>
|
||
82 | * A <code>WKTReader</code> is parameterized by a <code>GeometryFactory</code>,
|
||
83 | * to allow it to create <code>Geometry</code> objects of the appropriate
|
||
84 | * implementation. In particular, the <code>GeometryFactory</code>
|
||
85 | * determines the <code>PrecisionModel</code> and <code>SRID</code> that is
|
||
86 | * used. <P>
|
||
87 | * <p/>
|
||
88 | * The <code>WKTReader</code> converts all input numbers to the precise
|
||
89 | * internal representation.
|
||
90 | * <p/>
|
||
91 | * <h3>Notes:</h3>
|
||
92 | * <ul>
|
||
93 | * <li>The reader supports non-standard "LINEARRING" tags.
|
||
94 | * <li>The reader uses Double.parseDouble to perform the conversion of ASCII
|
||
95 | * numbers to floating point. This means it supports the Java
|
||
96 | * syntax for floating point literals (including scientific notation).
|
||
97 | * </ul>
|
||
98 | * <p/>
|
||
99 | * <h3>Syntax</h3>
|
||
100 | * The following syntax specification describes the version of Well-Known Text
|
||
101 | * supported by JTS.
|
||
102 | * (The specification uses a syntax language similar to that used in
|
||
103 | * the C and Java language specifications.)
|
||
104 | * <p/>
|
||
105 | * <p/>
|
||
106 | * <blockquote><pre>
|
||
107 | * <i>WKTGeometry:</i> one of<i>
|
||
108 | * <p/>
|
||
109 | * WKTPoint WKTLineString WKTLinearRing WKTPolygon
|
||
110 | * WKTMultiPoint WKTMultiLineString WKTMultiPolygon
|
||
111 | * WKTGeometryCollection</i>
|
||
112 | * <p/>
|
||
113 | * <i>WKTPoint:</i> <b>POINT ( </b><i>Coordinate</i> <b>)</b>
|
||
114 | * <p/>
|
||
115 | * <i>WKTLineString:</i> <b>LINESTRING</b> <i>CoordinateSequence</i>
|
||
116 | * <p/>
|
||
117 | * <i>WKTLinearRing:</i> <b>LINEARRING</b> <i>CoordinateSequence</i>
|
||
118 | * <p/>
|
||
119 | * <i>WKTPolygon:</i> <b>POLYGON</b> <i>CoordinateSequenceList</i>
|
||
120 | * <p/>
|
||
121 | * <i>WKTMultiPoint:</i> <b>MULTIPOINT</b> <i>CoordinateSequence</i>
|
||
122 | * <p/>
|
||
123 | * <i>WKTMultiLineString:</i> <b>MULTILINESTRING</b> <i>CoordinateSequenceList</i>
|
||
124 | * <p/>
|
||
125 | * <i>WKTMultiPolygon:</i>
|
||
126 | * <b>MULTIPOLYGON (</b> <i>CoordinateSequenceList {</i> , <i>CoordinateSequenceList }</i> <b>)</b>
|
||
127 | * <p/>
|
||
128 | * <i>WKTGeometryCollection: </i>
|
||
129 | * <b>GEOMETRYCOLLECTION (</b> <i>WKTGeometry {</i> , <i>WKTGeometry }</i> <b>)</b>
|
||
130 | * <p/>
|
||
131 | * <i>CoordinateSequenceList:</i>
|
||
132 | * <b>(</b> <i>CoordinateSequence {</i> <b>,</b> <i>CoordinateSequence }</i> <b>)</b>
|
||
133 | * <p/>
|
||
134 | * <i>CoordinateSequence:</i>
|
||
135 | * <b>(</b> <i>Coordinate {</i> , <i>Coordinate }</i> <b>)</b>
|
||
136 | * <p/>
|
||
137 | * <i>Coordinate:
|
||
138 | * Number Number Number<sub>opt</sub></i>
|
||
139 | * <p/>
|
||
140 | * <i>Number:</i> A Java-style floating-point number
|
||
141 | * <p/>
|
||
142 | * </pre></blockquote>
|
||
143 | *
|
||
144 | * @see WKTWriter
|
||
145 | */
|
||
146 | public class EWKTParser { |
||
147 | private static final String EMPTY = "EMPTY"; |
||
148 | private static final String COMMA = ","; |
||
149 | private static final String L_PAREN = "("; |
||
150 | private static final String R_PAREN = ")"; |
||
151 | private static final String EQUALS = "="; |
||
152 | private static final String SEMICOLON = ";"; |
||
153 | |||
154 | private static final String Z = "Z"; |
||
155 | private static final String ZM = "ZM"; |
||
156 | private static final String M = "M"; |
||
157 | |||
158 | private GeometryFactory geometryFactory;
|
||
159 | private PrecisionModel precisionModel;
|
||
160 | private StreamTokenizer tokenizer; |
||
161 | |||
162 | private int dimension = -1; |
||
163 | private Boolean hasM = null; |
||
164 | |||
165 | private GeometryManager manager = GeometryLocator.getGeometryManager();
|
||
166 | |||
167 | |||
168 | /**
|
||
169 | * Creates a reader that creates objects using the default {@link GeometryFactory}.
|
||
170 | */
|
||
171 | public EWKTParser() {
|
||
172 | this(new MGeometryFactory()); |
||
173 | } |
||
174 | |||
175 | /**
|
||
176 | * Creates a reader that creates objects using the given
|
||
177 | * {@link GeometryFactory}.
|
||
178 | *
|
||
179 | * @param geometryFactory the factory used to create <code>Geometry</code>s.
|
||
180 | */
|
||
181 | public EWKTParser(GeometryFactory geometryFactory) {
|
||
182 | this.geometryFactory = geometryFactory;
|
||
183 | precisionModel = geometryFactory.getPrecisionModel(); |
||
184 | } |
||
185 | |||
186 | /**
|
||
187 | * Reads a Well-Known Text representation of a {@link com.vividsolutions.jts.geom.Geometry}
|
||
188 | * from a {@link String}.
|
||
189 | *
|
||
190 | * @param wellKnownText one or more <Geometry Tagged Text>strings (see the OpenGIS
|
||
191 | * Simple Features Specification) separated by whitespace
|
||
192 | * @return a <code>Geometry</code> specified by <code>wellKnownText</code>
|
||
193 | * @throws com.vividsolutions.jts.io.ParseException
|
||
194 | * if a parsing problem occurs
|
||
195 | */
|
||
196 | public Geometry read(String wellKnownText) throws ParseException { |
||
197 | StringReader reader = new StringReader(wellKnownText); |
||
198 | try {
|
||
199 | return read(reader);
|
||
200 | } |
||
201 | finally {
|
||
202 | reader.close(); |
||
203 | } |
||
204 | } |
||
205 | |||
206 | /**
|
||
207 | * Reads a Well-Known Text representation of a {@link Geometry}
|
||
208 | * from a {@link java.io.Reader}.
|
||
209 | *
|
||
210 | * @param reader a Reader which will return a <Geometry Tagged Text>
|
||
211 | * string (see the OpenGIS Simple Features Specification)
|
||
212 | * @return a <code>Geometry</code> read from <code>reader</code>
|
||
213 | * @throws ParseException if a parsing problem occurs
|
||
214 | */
|
||
215 | public Geometry read(Reader reader) throws ParseException { |
||
216 | |||
217 | try {
|
||
218 | |||
219 | synchronized (this) { |
||
220 | if (this.tokenizer != null) { |
||
221 | throw new RuntimeException("EWKT-Reader is already in use."); |
||
222 | } |
||
223 | tokenizer = new StreamTokenizer(reader); |
||
224 | } |
||
225 | |||
226 | // set tokenizer to NOT parse numbers
|
||
227 | tokenizer.resetSyntax(); |
||
228 | tokenizer.wordChars('a', 'z'); |
||
229 | tokenizer.wordChars('A', 'Z'); |
||
230 | tokenizer.wordChars(128 + 32, 255); |
||
231 | tokenizer.wordChars('0', '9'); |
||
232 | tokenizer.wordChars('-', '-'); |
||
233 | tokenizer.wordChars('+', '+'); |
||
234 | tokenizer.wordChars('.', '.'); |
||
235 | tokenizer.whitespaceChars(0, ' '); |
||
236 | tokenizer.commentChar('#');
|
||
237 | |||
238 | this.hasM = null; |
||
239 | this.dimension = -1; |
||
240 | |||
241 | return readGeometryTaggedText();
|
||
242 | |||
243 | } catch (IOException | CreateGeometryException e) { |
||
244 | throw new ParseException(e.toString()); |
||
245 | } finally {
|
||
246 | this.tokenizer = null; |
||
247 | } |
||
248 | } |
||
249 | |||
250 | /**
|
||
251 | * Returns the next array of <code>Coordinate</code>s in the stream.
|
||
252 | *
|
||
253 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
254 | * format. The next element returned by the stream should be L_PAREN (the
|
||
255 | * beginning of "(x1 y1, x2 y2, ..., xn yn)") or EMPTY.
|
||
256 | * @return the next array of <code>Coordinate</code>s in the
|
||
257 | * stream, or an empty array if EMPTY is the next element returned by
|
||
258 | * the stream.
|
||
259 | * @throws IOException if an I/O error occurs
|
||
260 | * @throws ParseException if an unexpected token was encountered
|
||
261 | */
|
||
262 | private MCoordinate[] getCoordinates() |
||
263 | throws IOException, ParseException { |
||
264 | String nextToken = getNextEmptyOrOpenerOrDimension();
|
||
265 | if (nextToken.equals(EMPTY)) {
|
||
266 | return new MCoordinate[]{}; |
||
267 | } |
||
268 | if (nextToken.equals(Z)) {
|
||
269 | this.dimension = 3; |
||
270 | setHasM(false);
|
||
271 | nextToken = getNextEmptyOrOpener(); |
||
272 | if (nextToken.equals(EMPTY)) {
|
||
273 | return new MCoordinate[]{}; |
||
274 | } |
||
275 | } |
||
276 | if (nextToken.equals(M)) {
|
||
277 | this.dimension = 3; |
||
278 | setHasM(true);
|
||
279 | nextToken = getNextEmptyOrOpener(); |
||
280 | if (nextToken.equals(EMPTY)) {
|
||
281 | return new MCoordinate[]{}; |
||
282 | } |
||
283 | } |
||
284 | if (nextToken.equals(ZM)) {
|
||
285 | this.dimension = 4; |
||
286 | setHasM(true);
|
||
287 | nextToken = getNextEmptyOrOpener(); |
||
288 | if (nextToken.equals(EMPTY)) {
|
||
289 | return new MCoordinate[]{}; |
||
290 | } |
||
291 | } |
||
292 | |||
293 | ArrayList coordinates = new ArrayList(); |
||
294 | coordinates.add(getPreciseCoordinate()); |
||
295 | nextToken = getNextCloserOrComma(); |
||
296 | while (nextToken.equals(COMMA)) {
|
||
297 | coordinates.add(getPreciseCoordinate()); |
||
298 | nextToken = getNextCloserOrComma(); |
||
299 | } |
||
300 | return (MCoordinate[]) coordinates.toArray(new MCoordinate[coordinates.size()]); |
||
301 | } |
||
302 | |||
303 | /**
|
||
304 | * gets the next Coordinate and checks dimension
|
||
305 | *
|
||
306 | * @return
|
||
307 | * @throws IOException
|
||
308 | * @throws ParseException
|
||
309 | */
|
||
310 | private Coordinate getPreciseCoordinate()
|
||
311 | throws IOException, ParseException { |
||
312 | MCoordinate coord = new MCoordinate();
|
||
313 | coord.x = getNextNumber(); |
||
314 | coord.y = getNextNumber(); |
||
315 | |||
316 | Double thirdOrdinateValue = null; |
||
317 | Double fourthOrdinateValue = null; |
||
318 | |||
319 | if (this.dimension == 3) { |
||
320 | thirdOrdinateValue = getNextNumber(); |
||
321 | } else if (this.dimension == 4) { |
||
322 | thirdOrdinateValue = getNextNumber(); |
||
323 | fourthOrdinateValue = getNextNumber(); |
||
324 | } else if (this.dimension < 0) { |
||
325 | if (isNumberNext()) thirdOrdinateValue = getNextNumber();
|
||
326 | if (isNumberNext()) fourthOrdinateValue = getNextNumber();
|
||
327 | |||
328 | if (fourthOrdinateValue != null) { |
||
329 | this.dimension = 4; |
||
330 | setHasM(true);
|
||
331 | } else if (thirdOrdinateValue != null) { |
||
332 | this.dimension = 3; |
||
333 | setHasM(Boolean.TRUE.equals(this.hasM)); |
||
334 | } else {
|
||
335 | this.dimension = 2; |
||
336 | setHasM(false);
|
||
337 | } |
||
338 | } |
||
339 | |||
340 | switch (this.dimension) { |
||
341 | case 2: |
||
342 | break;
|
||
343 | case 3: |
||
344 | if (this.hasM) { |
||
345 | coord.m = thirdOrdinateValue; |
||
346 | } else {
|
||
347 | coord.z = thirdOrdinateValue; |
||
348 | } |
||
349 | break;
|
||
350 | case 4: |
||
351 | if (this.hasM) { |
||
352 | coord.z = thirdOrdinateValue; |
||
353 | coord.m = fourthOrdinateValue; |
||
354 | } else {
|
||
355 | throw new ParseException("Unsupported geometry dimension."); |
||
356 | } |
||
357 | break;
|
||
358 | default:
|
||
359 | throw new ParseException("Unsupported geometry dimension."); |
||
360 | } |
||
361 | |||
362 | precisionModel.makePrecise(coord); |
||
363 | return coord;
|
||
364 | } |
||
365 | |||
366 | |||
367 | private boolean isNumberNext() throws IOException { |
||
368 | int type = tokenizer.nextToken();
|
||
369 | tokenizer.pushBack(); |
||
370 | return type == StreamTokenizer.TT_WORD; |
||
371 | } |
||
372 | |||
373 | /**
|
||
374 | * Parses the next number in the stream.
|
||
375 | * Numbers with exponents are handled.
|
||
376 | *
|
||
377 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
378 | * format. The next token must be a number.
|
||
379 | * @return the next number in the stream
|
||
380 | * @throws ParseException if the next token is not a valid number
|
||
381 | * @throws IOException if an I/O error occurs
|
||
382 | */
|
||
383 | private double getNextNumber() throws IOException, |
||
384 | ParseException {
|
||
385 | int type = tokenizer.nextToken();
|
||
386 | switch (type) {
|
||
387 | case StreamTokenizer.TT_WORD: { |
||
388 | try {
|
||
389 | return Double.parseDouble(tokenizer.sval); |
||
390 | } |
||
391 | catch (NumberFormatException ex) { |
||
392 | throw new ParseException("Invalid number: " + tokenizer.sval); |
||
393 | } |
||
394 | } |
||
395 | } |
||
396 | parseError("number");
|
||
397 | return 0.0; |
||
398 | } |
||
399 | |||
400 | /**
|
||
401 | * Returns the next EMPTY or L_PAREN in the stream as uppercase text.
|
||
402 | *
|
||
403 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
404 | * format. The next token must be EMPTY or L_PAREN.
|
||
405 | * @return the next EMPTY or L_PAREN in the stream as uppercase
|
||
406 | * text.
|
||
407 | * @throws ParseException if the next token is not EMPTY or L_PAREN
|
||
408 | * @throws IOException if an I/O error occurs
|
||
409 | */
|
||
410 | private String getNextEmptyOrOpener() throws IOException, ParseException { |
||
411 | String nextWord = getNextWord();
|
||
412 | if (nextWord.equals(EMPTY) || nextWord.equals(L_PAREN)) {
|
||
413 | return nextWord;
|
||
414 | } |
||
415 | parseError(EMPTY + " or " + L_PAREN);
|
||
416 | return null; |
||
417 | } |
||
418 | |||
419 | /**
|
||
420 | * Returns the next EMPTY, L_PAREN, Z or ZM in the stream as uppercase text.
|
||
421 | *
|
||
422 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
423 | * format. The next token must be EMPTY or L_PAREN.
|
||
424 | * @return the next EMPTY or L_PAREN in the stream as uppercase
|
||
425 | * text.
|
||
426 | * @throws ParseException if the next token is not EMPTY or L_PAREN
|
||
427 | * @throws IOException if an I/O error occurs
|
||
428 | */
|
||
429 | private String getNextEmptyOrOpenerOrDimension() throws IOException, ParseException { |
||
430 | String nextWord = getNextWord();
|
||
431 | if (nextWord.equals(EMPTY) || nextWord.equals(L_PAREN) || nextWord.equalsIgnoreCase(Z) || nextWord.equalsIgnoreCase(M) || nextWord.equalsIgnoreCase(ZM)) {
|
||
432 | return nextWord;
|
||
433 | } |
||
434 | parseError(EMPTY + " or " + L_PAREN + " or " + Z + " or " + M + " or " + ZM + " or "); |
||
435 | return null; |
||
436 | } |
||
437 | |||
438 | /**
|
||
439 | * Returns the next R_PAREN or COMMA in the stream.
|
||
440 | *
|
||
441 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
442 | * format. The next token must be R_PAREN or COMMA.
|
||
443 | * @return the next R_PAREN or COMMA in the stream
|
||
444 | * @throws ParseException if the next token is not R_PAREN or COMMA
|
||
445 | * @throws IOException if an I/O error occurs
|
||
446 | */
|
||
447 | private String getNextCloserOrComma() throws IOException, ParseException { |
||
448 | String nextWord = getNextWord();
|
||
449 | if (nextWord.equals(COMMA) || nextWord.equals(R_PAREN)) {
|
||
450 | return nextWord;
|
||
451 | } |
||
452 | parseError(COMMA + " or " + R_PAREN);
|
||
453 | return null; |
||
454 | } |
||
455 | |||
456 | /**
|
||
457 | * Returns the next R_PAREN in the stream.
|
||
458 | *
|
||
459 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
460 | * format. The next token must be R_PAREN.
|
||
461 | * @return the next R_PAREN in the stream
|
||
462 | * @throws ParseException if the next token is not R_PAREN
|
||
463 | * @throws IOException if an I/O error occurs
|
||
464 | */
|
||
465 | private String getNextCloser() throws IOException, ParseException { |
||
466 | String nextWord = getNextWord();
|
||
467 | if (nextWord.equals(R_PAREN)) {
|
||
468 | return nextWord;
|
||
469 | } |
||
470 | parseError(R_PAREN); |
||
471 | return null; |
||
472 | } |
||
473 | |||
474 | /**
|
||
475 | * Returns the next R_PAREN in the stream.
|
||
476 | *
|
||
477 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
478 | * format. The next token must be R_PAREN.
|
||
479 | * @return the next R_PAREN in the stream
|
||
480 | * @throws ParseException if the next token is not R_PAREN
|
||
481 | * @throws IOException if an I/O error occurs
|
||
482 | */
|
||
483 | private int getSRID() throws IOException, ParseException { |
||
484 | if (!getNextWord().equals(EQUALS)) {
|
||
485 | parseError(EQUALS); |
||
486 | return 0; |
||
487 | } |
||
488 | int srid = Integer.parseInt(getNextWord()); |
||
489 | if (!getNextWord().equals(SEMICOLON)) {
|
||
490 | parseError(SEMICOLON); |
||
491 | return 0; |
||
492 | } |
||
493 | return srid;
|
||
494 | } |
||
495 | |||
496 | /**
|
||
497 | * Returns the next word in the stream.
|
||
498 | *
|
||
499 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
500 | * format. The next token must be a word.
|
||
501 | * @return the next word in the stream as uppercase text
|
||
502 | * @throws ParseException if the next token is not a word
|
||
503 | * @throws IOException if an I/O error occurs
|
||
504 | */
|
||
505 | private String getNextWord() throws IOException, ParseException { |
||
506 | int type = tokenizer.nextToken();
|
||
507 | switch (type) {
|
||
508 | case StreamTokenizer.TT_WORD: |
||
509 | |||
510 | String word = tokenizer.sval;
|
||
511 | if (word.equalsIgnoreCase(EMPTY))
|
||
512 | return EMPTY;
|
||
513 | return word;
|
||
514 | |||
515 | case '(': |
||
516 | return L_PAREN;
|
||
517 | case ')': |
||
518 | return R_PAREN;
|
||
519 | case ',': |
||
520 | return COMMA;
|
||
521 | case '=': |
||
522 | return EQUALS;
|
||
523 | case ';': |
||
524 | return SEMICOLON;
|
||
525 | } |
||
526 | parseError("word");
|
||
527 | return null; |
||
528 | } |
||
529 | |||
530 | /**
|
||
531 | * Throws a formatted ParseException for the current token.
|
||
532 | *
|
||
533 | * @param expected a description of what was expected
|
||
534 | * @throws ParseException
|
||
535 | * @throws com.vividsolutions.jts.util.AssertionFailedException
|
||
536 | * if an invalid token is encountered
|
||
537 | */
|
||
538 | private void parseError(String expected) |
||
539 | throws ParseException { |
||
540 | // throws Asserts for tokens that should never be seen
|
||
541 | if (tokenizer.ttype == StreamTokenizer.TT_NUMBER) |
||
542 | Assert.shouldNeverReachHere("Unexpected NUMBER token");
|
||
543 | if (tokenizer.ttype == StreamTokenizer.TT_EOL) |
||
544 | Assert.shouldNeverReachHere("Unexpected EOL token");
|
||
545 | |||
546 | String tokenStr = tokenString();
|
||
547 | throw new ParseException("Expected " + expected + " but found " + tokenStr); |
||
548 | } |
||
549 | |||
550 | /**
|
||
551 | * Gets a description of the current token
|
||
552 | *
|
||
553 | * @return a description of the current token
|
||
554 | */
|
||
555 | private String tokenString() { |
||
556 | switch (tokenizer.ttype) {
|
||
557 | case StreamTokenizer.TT_NUMBER: |
||
558 | return "<NUMBER>"; |
||
559 | case StreamTokenizer.TT_EOL: |
||
560 | return "End-of-Line"; |
||
561 | case StreamTokenizer.TT_EOF: |
||
562 | return "End-of-Stream"; |
||
563 | case StreamTokenizer.TT_WORD: |
||
564 | return "'" + tokenizer.sval + "'"; |
||
565 | } |
||
566 | return "'" + (char) tokenizer.ttype + "'"; |
||
567 | } |
||
568 | |||
569 | /**
|
||
570 | * Creates a <code>Geometry</code> using the next token in the stream.
|
||
571 | *
|
||
572 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
573 | * format. The next tokens must form a <Geometry Tagged Text>.
|
||
574 | * @return a <code>Geometry</code> specified by the next token
|
||
575 | * in the stream
|
||
576 | * @throws ParseException if the coordinates used to create a <code>Polygon</code>
|
||
577 | * shell and holes do not form closed linestrings, or if an unexpected
|
||
578 | * token was encountered
|
||
579 | * @throws IOException if an I/O error occurs
|
||
580 | * @throws CreateGeometryException
|
||
581 | */
|
||
582 | private Geometry readGeometryTaggedText() throws IOException, ParseException, CreateGeometryException { |
||
583 | |||
584 | String type = null; |
||
585 | Geometry geom; |
||
586 | |||
587 | int srid = geometryFactory.getSRID();
|
||
588 | |||
589 | try {
|
||
590 | String firstWord = getNextWord();
|
||
591 | if ("SRID".equals(firstWord)) { |
||
592 | srid = getSRID(); |
||
593 | type = getNextWord(); |
||
594 | } else {
|
||
595 | type = firstWord; |
||
596 | } |
||
597 | } catch (IOException e) { |
||
598 | return null; |
||
599 | } catch (ParseException e) { |
||
600 | return null; |
||
601 | } |
||
602 | |||
603 | if (type.equals("POINT")) { |
||
604 | geom = readPointText(); |
||
605 | } else if (type.equals("POINTZ")) { |
||
606 | this.dimension = 3; |
||
607 | geom = readPointText(); |
||
608 | } else if (type.equals("POINTM")) { |
||
609 | setHasM(true);
|
||
610 | geom = readPointText(); |
||
611 | } else if (type.equals("POINTZM")) { |
||
612 | setHasM(true);
|
||
613 | this.dimension = 4; |
||
614 | geom = readPointText(); |
||
615 | } else if (type.equalsIgnoreCase("LINESTRING")) { |
||
616 | geom = readLineStringText(); |
||
617 | } else if (type.equalsIgnoreCase("LINESTRINGZ")) { |
||
618 | this.dimension = 3; |
||
619 | geom = readLineStringText(); |
||
620 | } else if (type.equalsIgnoreCase("LINESTRINGM")) { |
||
621 | setHasM(true);
|
||
622 | this.dimension = 3; |
||
623 | geom = readLineStringText(); |
||
624 | } else if (type.equalsIgnoreCase("LINESTRINGZM")) { |
||
625 | setHasM(true);
|
||
626 | this.dimension = 4; |
||
627 | geom = readLineStringText(); |
||
628 | } else if (type.equalsIgnoreCase("LINEARRING")) { |
||
629 | geom = readLinearRingText(); |
||
630 | } else if (type.equalsIgnoreCase("LINEARRINGZ")) { |
||
631 | this.dimension = 3; |
||
632 | geom = readLinearRingText(); |
||
633 | } else if (type.equalsIgnoreCase("LINEARRINGM")) { |
||
634 | setHasM(true);
|
||
635 | this.dimension = 3; |
||
636 | geom = readLinearRingText(); |
||
637 | } else if (type.equalsIgnoreCase("LINEARRINGZM")) { |
||
638 | setHasM(true);
|
||
639 | this.dimension = 4; |
||
640 | geom = readLinearRingText(); |
||
641 | } else if (type.equalsIgnoreCase("POLYGON")) { |
||
642 | geom = readPolygonText(); |
||
643 | } else if (type.equalsIgnoreCase("POLYGONZ")) { |
||
644 | this.dimension = 3; |
||
645 | geom = readPolygonText(); |
||
646 | } else if (type.equalsIgnoreCase("POLYGONM")) { |
||
647 | setHasM(true);
|
||
648 | this.dimension = 3; |
||
649 | geom = readPolygonText(); |
||
650 | // throw new RuntimeException("PolygonM is not supported.");
|
||
651 | } else if (type.equalsIgnoreCase("POLYGONZM")) { |
||
652 | setHasM(true);
|
||
653 | this.dimension = 4; |
||
654 | geom = readPolygonText(); |
||
655 | // throw new RuntimeException("PolygonM is not supported.");
|
||
656 | } else if (type.equalsIgnoreCase("MULTIPOINT")) { |
||
657 | geom = readMultiPointText(); |
||
658 | } else if (type.equalsIgnoreCase("MULTIPOINTZ")) { |
||
659 | this.dimension = 3; |
||
660 | geom = readMultiPointText(); |
||
661 | } else if (type.equalsIgnoreCase("MULTIPOINTM")) { |
||
662 | setHasM(true);
|
||
663 | this.dimension = 3; |
||
664 | geom = readMultiPointText(); |
||
665 | } else if (type.equalsIgnoreCase("MULTIPOINTZM")) { |
||
666 | setHasM(true);
|
||
667 | this.dimension = 4; |
||
668 | geom = readMultiPointText(); |
||
669 | } else if (type.equalsIgnoreCase("MULTILINESTRING")) { |
||
670 | geom = readMultiLineStringText(); |
||
671 | } else if (type.equalsIgnoreCase("MULTILINESTRINGZ")) { |
||
672 | this.dimension = 3; |
||
673 | geom = readMultiLineStringText(); |
||
674 | } else if (type.equalsIgnoreCase("MULTILINESTRINGM")) { |
||
675 | setHasM(true);
|
||
676 | this.dimension = 3; |
||
677 | geom = readMultiLineStringText(); |
||
678 | } else if (type.equalsIgnoreCase("MULTILINESTRINGZM")) { |
||
679 | setHasM(true);
|
||
680 | this.dimension = 4; |
||
681 | geom = readMultiLineStringText(); |
||
682 | } else if (type.equalsIgnoreCase("MULTIPOLYGON")) { |
||
683 | geom = readMultiPolygonText(); |
||
684 | } else if (type.equalsIgnoreCase("MULTIPOLYGONZ")) { |
||
685 | this.dimension = 3; |
||
686 | geom = readMultiPolygonText(); |
||
687 | } else if (type.equalsIgnoreCase("MULTIPOLYGONM")) { |
||
688 | setHasM(true);
|
||
689 | this.dimension = 3; |
||
690 | geom = readMultiPolygonText(); |
||
691 | } else if (type.equalsIgnoreCase("MULTIPOLYGONZM")) { |
||
692 | setHasM(true);
|
||
693 | this.dimension = 4; |
||
694 | geom = readMultiPolygonText(); |
||
695 | // throw new RuntimeException("MultiPolygonM is not supported.");
|
||
696 | // } else if (type.equalsIgnoreCase("GEOMETRYCOLLECTION")) {
|
||
697 | // geom = readGeometryCollectionText();
|
||
698 | // } else if (type.equalsIgnoreCase("GEOMETRYCOLLECTIONM")) {
|
||
699 | // setHasM(true);
|
||
700 | // geom = readGeometryCollectionText();
|
||
701 | } else {
|
||
702 | throw new ParseException("Unknown geometry type: " + type); |
||
703 | } |
||
704 | // geom.setSRID(srid);
|
||
705 | |||
706 | return geom;
|
||
707 | } |
||
708 | |||
709 | /**
|
||
710 | * m-values sicherstellen
|
||
711 | *
|
||
712 | * @throws ParseException
|
||
713 | */
|
||
714 | private void setHasM(boolean hasM) throws ParseException { |
||
715 | if (this.hasM == null) { |
||
716 | this.hasM = hasM;
|
||
717 | } else if (this.hasM != hasM) { |
||
718 | 47432 | fdiaz | throw new ParseException("Inconsistent use of m-values."); |
719 | 42875 | fdiaz | } |
720 | } |
||
721 | |||
722 | /**
|
||
723 | * Creates a <code>Point</code> using the next token in the stream.
|
||
724 | *
|
||
725 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
726 | * format. The next tokens must form a <Point Text>.
|
||
727 | * @return a <code>Point</code> specified by the next token in
|
||
728 | * the stream
|
||
729 | * @throws IOException if an I/O error occurs
|
||
730 | * @throws ParseException if an unexpected token was encountered
|
||
731 | * @throws LocatorException
|
||
732 | * @throws CreateGeometryException
|
||
733 | */
|
||
734 | private Point readPointText() throws IOException, ParseException, CreateGeometryException { |
||
735 | |||
736 | String nextToken = getNextEmptyOrOpenerOrDimension();
|
||
737 | if (nextToken.equals(EMPTY)) {
|
||
738 | return (Point)manager.create(TYPES.POINT, this.getSubtype()); |
||
739 | } |
||
740 | if (nextToken.equals(Z)) {
|
||
741 | this.dimension = 3; |
||
742 | setHasM(false);
|
||
743 | nextToken = getNextEmptyOrOpener(); |
||
744 | if (nextToken.equals(EMPTY)) {
|
||
745 | return (Point)manager.create(TYPES.POINT, this.getSubtype()); |
||
746 | } |
||
747 | |||
748 | }else if (nextToken.equals(M)) { |
||
749 | this.dimension = 3; |
||
750 | setHasM(true);
|
||
751 | nextToken = getNextEmptyOrOpener(); |
||
752 | if (nextToken.equals(EMPTY)) {
|
||
753 | return (Point)manager.create(TYPES.POINT, this.getSubtype()); |
||
754 | } |
||
755 | |||
756 | }else if (nextToken.equals(ZM)) { |
||
757 | this.dimension = 4; |
||
758 | setHasM(true);
|
||
759 | nextToken = getNextEmptyOrOpener(); |
||
760 | if (nextToken.equals(EMPTY)) {
|
||
761 | return (Point)manager.create(TYPES.POINT, this.getSubtype()); |
||
762 | } |
||
763 | |||
764 | } |
||
765 | |||
766 | int subtype = this.getSubtype(); |
||
767 | Coordinate coordinates = getPreciseCoordinate(); |
||
768 | Point point = (Point)manager.create(TYPES.POINT, subtype); |
||
769 | fillPoint(subtype, coordinates, point); |
||
770 | getNextCloser(); |
||
771 | return point;
|
||
772 | } |
||
773 | |||
774 | /**
|
||
775 | * @param subtype
|
||
776 | * @param coordinates
|
||
777 | * @param point
|
||
778 | */
|
||
779 | private void fillPoint(int subtype, Coordinate coordinates, Point point) { |
||
780 | point.setX(coordinates.x); |
||
781 | point.setY(coordinates.y); |
||
782 | if (subtype == Geometry.SUBTYPES.GEOM2DM) {
|
||
783 | 47432 | fdiaz | point.setCoordinateAt(2, coordinates.getOrdinate(CoordinateSequence.M));
|
784 | 42875 | fdiaz | } else if (subtype == Geometry.SUBTYPES.GEOM3DM) { |
785 | 47432 | fdiaz | point.setCoordinateAt(2, coordinates.getOrdinate(CoordinateSequence.Z));
|
786 | point.setCoordinateAt(3, coordinates.getOrdinate(CoordinateSequence.M));
|
||
787 | 42875 | fdiaz | } else if (subtype == Geometry.SUBTYPES.GEOM3D) { |
788 | 47432 | fdiaz | point.setCoordinateAt(2, coordinates.getOrdinate(CoordinateSequence.Z));
|
789 | 42875 | fdiaz | } |
790 | } |
||
791 | |||
792 | /**
|
||
793 | * @return
|
||
794 | */
|
||
795 | private int getSubtype() { |
||
796 | int subtype;
|
||
797 | switch (this.dimension) { |
||
798 | case 4: |
||
799 | subtype = SUBTYPES.GEOM3DM; |
||
800 | break;
|
||
801 | case 3: |
||
802 | if(this.hasM){ |
||
803 | subtype = SUBTYPES.GEOM2DM; |
||
804 | } else {
|
||
805 | subtype = SUBTYPES.GEOM3D; |
||
806 | } |
||
807 | break;
|
||
808 | default:
|
||
809 | subtype = SUBTYPES.GEOM2D; |
||
810 | } |
||
811 | return subtype;
|
||
812 | } |
||
813 | |||
814 | /**
|
||
815 | * Creates a <code>LineString</code> using the next token in the stream.
|
||
816 | *
|
||
817 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
818 | * format. The next tokens must form a <LineString Text>.
|
||
819 | * @return a <code>LineString</code> specified by the next
|
||
820 | * token in the stream
|
||
821 | * @throws IOException if an I/O error occurs
|
||
822 | * @throws ParseException if an unexpected token was encountered
|
||
823 | * @throws CreateGeometryException
|
||
824 | */
|
||
825 | private Line readLineStringText() throws IOException, ParseException, CreateGeometryException { |
||
826 | |||
827 | MCoordinate[] coords = getCoordinates();
|
||
828 | Line line = (Line) manager.create(TYPES.LINE, getSubtype()); |
||
829 | |||
830 | int subtype = this.getSubtype(); |
||
831 | for(int i=0; i<coords.length; i++){ |
||
832 | Point point = (Point)manager.create(TYPES.POINT, getSubtype()); |
||
833 | fillPoint(subtype, coords[i], point); |
||
834 | line.addVertex(point); |
||
835 | } |
||
836 | return line;
|
||
837 | } |
||
838 | |||
839 | /**
|
||
840 | * Creates a <code>LinearRing</code> using the next token in the stream.
|
||
841 | *
|
||
842 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
843 | * format. The next tokens must form a <LineString Text>.
|
||
844 | * @return a <code>LinearRing</code> specified by the next
|
||
845 | * token in the stream
|
||
846 | * @throws IOException if an I/O error occurs
|
||
847 | * @throws ParseException if the coordinates used to create the <code>LinearRing</code>
|
||
848 | * do not form a closed linestring, or if an unexpected token was
|
||
849 | * encountered
|
||
850 | * @throws CreateGeometryException
|
||
851 | */
|
||
852 | private Ring readLinearRingText()
|
||
853 | throws IOException, ParseException, CreateGeometryException { |
||
854 | MCoordinate[] coords = getCoordinates();
|
||
855 | int subtype = getSubtype();
|
||
856 | Ring ring = (Ring) manager.create(TYPES.RING, subtype); |
||
857 | for (int i=0; i < coords.length; i++) |
||
858 | { |
||
859 | Point point = (Point)manager.create(TYPES.POINT, subtype); |
||
860 | fillPoint(subtype, coords[i], point); |
||
861 | ring.addVertex(point); |
||
862 | } |
||
863 | return ring;
|
||
864 | } |
||
865 | |||
866 | /**
|
||
867 | * Creates a <code>MultiPoint</code> using the next token in the stream.
|
||
868 | *
|
||
869 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
870 | * format. The next tokens must form a <MultiPoint Text>.
|
||
871 | * @return a <code>MultiPoint</code> specified by the next
|
||
872 | * token in the stream
|
||
873 | * @throws IOException if an I/O error occurs
|
||
874 | * @throws ParseException if an unexpected token was encountered
|
||
875 | * @throws CreateGeometryException
|
||
876 | 47432 | fdiaz | */
|
877 | private MultiPoint readMultiPointText() throws IOException, ParseException, CreateGeometryException { |
||
878 | String nextToken = getNextEmptyOrOpenerOrDimension();
|
||
879 | if (nextToken.equals(Z)) {
|
||
880 | this.dimension = 3; |
||
881 | setHasM(false);
|
||
882 | nextToken = getNextEmptyOrOpener(); |
||
883 | } else if (nextToken.equals(M)) { |
||
884 | this.dimension = 3; |
||
885 | setHasM(true);
|
||
886 | nextToken = getNextEmptyOrOpener(); |
||
887 | } else if (nextToken.equals(ZM)) { |
||
888 | this.dimension = 4; |
||
889 | setHasM(true);
|
||
890 | nextToken = getNextEmptyOrOpener(); |
||
891 | } else {
|
||
892 | setHasM(false);
|
||
893 | this.dimension = 2; |
||
894 | } |
||
895 | |||
896 | 42875 | fdiaz | int subtype = getSubtype();
|
897 | MultiPoint multiPoint = (MultiPoint)manager.create(TYPES.MULTIPOINT, subtype); |
||
898 | 47432 | fdiaz | if (nextToken.equals(EMPTY)) {
|
899 | return multiPoint;
|
||
900 | 42875 | fdiaz | } |
901 | 47432 | fdiaz | |
902 | int token = tokenizer.nextToken();
|
||
903 | tokenizer.pushBack(); |
||
904 | if(token == '('){ |
||
905 | String stoken;
|
||
906 | do {
|
||
907 | Point p = readPointText();
|
||
908 | multiPoint.addPoint(p); |
||
909 | stoken = getNextCloserOrComma(); |
||
910 | } while (StringUtils.equalsIgnoreCase(stoken, COMMA));
|
||
911 | } else {
|
||
912 | String stoken;
|
||
913 | do {
|
||
914 | Coordinate coordinates = getPreciseCoordinate(); |
||
915 | Point point = (Point) manager.create(TYPES.POINT, subtype); |
||
916 | fillPoint(subtype, coordinates, point); |
||
917 | multiPoint.addPoint(point); |
||
918 | stoken = getNextCloserOrComma(); |
||
919 | } while (StringUtils.equalsIgnoreCase(stoken, COMMA));
|
||
920 | } |
||
921 | 42875 | fdiaz | return multiPoint;
|
922 | } |
||
923 | |||
924 | /**
|
||
925 | * Creates an array of <code>Point</code>s having the given <code>Coordinate</code>
|
||
926 | * s.
|
||
927 | *
|
||
928 | * @param coordinates the <code>Coordinate</code>s with which to create the
|
||
929 | * <code>Point</code>s
|
||
930 | * @return <code>Point</code>s created using this <code>WKTReader</code>
|
||
931 | * s <code>GeometryFactory</code>
|
||
932 | */
|
||
933 | private Point[] toPoints(Coordinate[] coordinates) { |
||
934 | ArrayList points = new ArrayList(); |
||
935 | for (int i = 0; i < coordinates.length; i++) { |
||
936 | points.add(geometryFactory.createPoint(coordinates[i])); |
||
937 | } |
||
938 | return (Point[]) points.toArray(new Point[]{}); |
||
939 | } |
||
940 | |||
941 | /**
|
||
942 | * Creates a <code>Polygon</code> using the next token in the stream.
|
||
943 | *
|
||
944 | * @param hasM
|
||
945 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
946 | * format. The next tokens must form a <Polygon Text>.
|
||
947 | * @return a <code>Polygon</code> specified by the next token
|
||
948 | * in the stream
|
||
949 | * @throws ParseException if the coordinates used to create the <code>Polygon</code>
|
||
950 | * shell and holes do not form closed linestrings, or if an unexpected
|
||
951 | * token was encountered.
|
||
952 | * @throws IOException if an I/O error occurs
|
||
953 | * @throws CreateGeometryException
|
||
954 | */
|
||
955 | private Polygon readPolygonText() throws IOException, ParseException, CreateGeometryException { |
||
956 | |||
957 | |||
958 | // // PolygonM is not supported
|
||
959 | // setHasM(false);
|
||
960 | //
|
||
961 | String nextToken = getNextEmptyOrOpenerOrDimension();
|
||
962 | if (nextToken.equals(EMPTY)) {
|
||
963 | return (Polygon) manager.create(TYPES.POLYGON, getSubtype()); |
||
964 | } |
||
965 | if (nextToken.equals(Z)) {
|
||
966 | this.dimension = 3; |
||
967 | setHasM(false);
|
||
968 | nextToken = getNextEmptyOrOpener(); |
||
969 | if (nextToken.equals(EMPTY)) {
|
||
970 | return (Polygon) manager.create(TYPES.POLYGON, getSubtype()); |
||
971 | } |
||
972 | } |
||
973 | if (nextToken.equals(M)) {
|
||
974 | this.dimension = 3; |
||
975 | setHasM(true);
|
||
976 | nextToken = getNextEmptyOrOpener(); |
||
977 | if (nextToken.equals(EMPTY)) {
|
||
978 | return (Polygon) manager.create(TYPES.POLYGON, getSubtype()); |
||
979 | } |
||
980 | } |
||
981 | if (nextToken.equals(ZM)) {
|
||
982 | this.dimension = 4; |
||
983 | setHasM(true);
|
||
984 | nextToken = getNextEmptyOrOpener(); |
||
985 | if (nextToken.equals(EMPTY)) {
|
||
986 | return (Polygon) manager.create(TYPES.POLYGON, getSubtype()); |
||
987 | } |
||
988 | } |
||
989 | |||
990 | Polygon polygon = (Polygon) manager.create(TYPES.POLYGON, getSubtype()); |
||
991 | |||
992 | ArrayList holes = new ArrayList(); |
||
993 | Ring shell = readLinearRingText(); |
||
994 | for (int i = 0; i < shell.getNumVertices(); i++) { |
||
995 | polygon.addVertex(shell.getVertex(i)); |
||
996 | } |
||
997 | |||
998 | nextToken = getNextCloserOrComma(); |
||
999 | while (nextToken.equals(COMMA)) {
|
||
1000 | Ring hole = readLinearRingText(); |
||
1001 | polygon.addInteriorRing(hole); |
||
1002 | nextToken = getNextCloserOrComma(); |
||
1003 | } |
||
1004 | return polygon;
|
||
1005 | } |
||
1006 | |||
1007 | /**
|
||
1008 | * Creates a <code>MultiLineString</code> using the next token in the stream.
|
||
1009 | *
|
||
1010 | * @param hasM
|
||
1011 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
1012 | * format. The next tokens must form a <MultiLineString Text>.
|
||
1013 | * @return a <code>MultiLineString</code> specified by the
|
||
1014 | * next token in the stream
|
||
1015 | * @throws IOException if an I/O error occurs
|
||
1016 | * @throws ParseException if an unexpected token was encountered
|
||
1017 | * @throws CreateGeometryException
|
||
1018 | */
|
||
1019 | private MultiLine readMultiLineStringText() throws IOException, ParseException, CreateGeometryException { |
||
1020 | |||
1021 | String nextToken = getNextEmptyOrOpenerOrDimension();
|
||
1022 | if (nextToken.equals(EMPTY)) {
|
||
1023 | 46350 | fdiaz | return (MultiLine)manager.create(TYPES.MULTILINE, getSubtype());
|
1024 | 42875 | fdiaz | } |
1025 | if (nextToken.equals(Z)) {
|
||
1026 | this.dimension = 3; |
||
1027 | setHasM(false);
|
||
1028 | nextToken = getNextEmptyOrOpener(); |
||
1029 | if (nextToken.equals(EMPTY)) {
|
||
1030 | return (MultiLine)manager.create(TYPES.MULTILINE, getSubtype());
|
||
1031 | } |
||
1032 | |||
1033 | } |
||
1034 | if (nextToken.equals(M)) {
|
||
1035 | this.dimension = 3; |
||
1036 | setHasM(true);
|
||
1037 | nextToken = getNextEmptyOrOpener(); |
||
1038 | if (nextToken.equals(EMPTY)) {
|
||
1039 | return (MultiLine)manager.create(TYPES.MULTILINE, getSubtype());
|
||
1040 | } |
||
1041 | } |
||
1042 | if (nextToken.equals(ZM)) {
|
||
1043 | this.dimension = 4; |
||
1044 | setHasM(true);
|
||
1045 | nextToken = getNextEmptyOrOpener(); |
||
1046 | if (nextToken.equals(EMPTY)) {
|
||
1047 | return (MultiLine)manager.create(TYPES.MULTILINE, getSubtype());
|
||
1048 | } |
||
1049 | } |
||
1050 | |||
1051 | 46350 | fdiaz | MultiLine multiline = (MultiLine)manager.create(TYPES.MULTILINE, getSubtype()); |
1052 | |||
1053 | |||
1054 | 42875 | fdiaz | Line line = readLineStringText();
|
1055 | if(line!=null){ |
||
1056 | multiline.addCurve(line); |
||
1057 | } |
||
1058 | nextToken = getNextCloserOrComma(); |
||
1059 | while (nextToken.equals(COMMA)) {
|
||
1060 | line = readLineStringText(); |
||
1061 | if(line!=null){ |
||
1062 | multiline.addCurve(line); |
||
1063 | } |
||
1064 | nextToken = getNextCloserOrComma(); |
||
1065 | } |
||
1066 | |||
1067 | return multiline;
|
||
1068 | } |
||
1069 | |||
1070 | /**
|
||
1071 | * Creates a <code>MultiPolygon</code> using the next token in the stream.
|
||
1072 | *
|
||
1073 | * @param hasM
|
||
1074 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
1075 | * format. The next tokens must form a <MultiPolygon Text>.
|
||
1076 | * @return a <code>MultiPolygon</code> specified by the next
|
||
1077 | * token in the stream, or if if the coordinates used to create the
|
||
1078 | * <code>Polygon</code> shells and holes do not form closed linestrings.
|
||
1079 | * @throws IOException if an I/O error occurs
|
||
1080 | * @throws ParseException if an unexpected token was encountered
|
||
1081 | * @throws CreateGeometryException
|
||
1082 | */
|
||
1083 | private MultiPolygon readMultiPolygonText()
|
||
1084 | throws IOException, ParseException, CreateGeometryException { |
||
1085 | |||
1086 | |||
1087 | String nextToken = getNextEmptyOrOpenerOrDimension();
|
||
1088 | if (nextToken.equals(Z)) {
|
||
1089 | this.dimension = 3; |
||
1090 | setHasM(false);
|
||
1091 | nextToken = getNextEmptyOrOpener(); |
||
1092 | 47432 | fdiaz | } else if (nextToken.equals(M)) { |
1093 | 42875 | fdiaz | this.dimension = 3; |
1094 | setHasM(true);
|
||
1095 | nextToken = getNextEmptyOrOpener(); |
||
1096 | 47432 | fdiaz | } else if (nextToken.equals(ZM)) { |
1097 | 42875 | fdiaz | this.dimension = 4; |
1098 | setHasM(true);
|
||
1099 | nextToken = getNextEmptyOrOpener(); |
||
1100 | 47432 | fdiaz | } else {
|
1101 | setHasM(false);
|
||
1102 | this.dimension = 2; |
||
1103 | 42875 | fdiaz | } |
1104 | |||
1105 | 47432 | fdiaz | MultiPolygon multiPolygon = (MultiPolygon)manager.create(TYPES.MULTIPOLYGON, getSubtype()); |
1106 | if (nextToken.equals(EMPTY)) {
|
||
1107 | return multiPolygon;
|
||
1108 | } |
||
1109 | |||
1110 | 42875 | fdiaz | Polygon polygon = readPolygonText();
|
1111 | |||
1112 | multiPolygon.addSurface(polygon); |
||
1113 | nextToken = getNextCloserOrComma(); |
||
1114 | while (nextToken.equals(COMMA)) {
|
||
1115 | polygon = readPolygonText(); |
||
1116 | multiPolygon.addSurface(polygon); |
||
1117 | nextToken = getNextCloserOrComma(); |
||
1118 | } |
||
1119 | return multiPolygon;
|
||
1120 | } |
||
1121 | |||
1122 | /**
|
||
1123 | * Creates a <code>GeometryCollection</code> using the next token in the
|
||
1124 | * stream.
|
||
1125 | *
|
||
1126 | * @param tokenizer tokenizer over a stream of text in Well-known Text
|
||
1127 | * format. The next tokens must form a <GeometryCollection Text>.
|
||
1128 | * @return a <code>GeometryCollection</code> specified by the
|
||
1129 | * next token in the stream
|
||
1130 | * @throws ParseException if the coordinates used to create a <code>Polygon</code>
|
||
1131 | * shell and holes do not form closed linestrings, or if an unexpected
|
||
1132 | * token was encountered
|
||
1133 | * @throws IOException if an I/O error occurs
|
||
1134 | */
|
||
1135 | // private GeometryCollection readGeometryCollectionText()
|
||
1136 | // throws IOException, ParseException {
|
||
1137 | //
|
||
1138 | // String nextToken = getNextEmptyOrOpener();
|
||
1139 | // if (nextToken.equals(EMPTY)) {
|
||
1140 | // return geometryFactory.createGeometryCollection(new Geometry[]{});
|
||
1141 | // }
|
||
1142 | // ArrayList geometries = new ArrayList();
|
||
1143 | // Geometry geometry = readGeometryTaggedText();
|
||
1144 | // geometries.add(geometry);
|
||
1145 | // nextToken = getNextCloserOrComma();
|
||
1146 | // while (nextToken.equals(COMMA)) {
|
||
1147 | // geometry = readGeometryTaggedText();
|
||
1148 | // geometries.add(geometry);
|
||
1149 | // nextToken = getNextCloserOrComma();
|
||
1150 | // }
|
||
1151 | // Geometry[] array = new Geometry[geometries.size()];
|
||
1152 | // return geometryFactory.createGeometryCollection((Geometry[]) geometries.toArray(array));
|
||
1153 | // }
|
||
1154 | |||
1155 | } |