Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.geometry / org.gvsig.fmap.geometry.operation / src / main / java / org / gvsig / fmap / geom / operation / fromwkt / WKTParser.java @ 40664

History | View | Annotate | Download (22.3 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.geom.operation.fromwkt;
25

    
26
import java.io.IOException;
27
import java.io.Reader;
28
import java.io.StreamTokenizer;
29
import java.io.StringReader;
30
import java.util.ArrayList;
31

    
32
import org.gvsig.fmap.geom.Geometry;
33
import org.gvsig.fmap.geom.GeometryException;
34
import org.gvsig.fmap.geom.GeometryLocator;
35
import org.gvsig.fmap.geom.GeometryManager;
36
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
37
import org.gvsig.fmap.geom.Geometry.TYPES;
38
import org.gvsig.fmap.geom.aggregate.MultiCurve;
39
import org.gvsig.fmap.geom.aggregate.MultiPoint;
40
import org.gvsig.fmap.geom.aggregate.MultiSurface;
41
import org.gvsig.fmap.geom.exception.CreateGeometryException;
42
import org.gvsig.fmap.geom.operation.GeometryOperationException;
43
import org.gvsig.fmap.geom.primitive.Curve;
44
import org.gvsig.fmap.geom.primitive.GeneralPathX;
45
import org.gvsig.fmap.geom.primitive.Point;
46
import org.gvsig.fmap.geom.primitive.Surface;
47
import org.gvsig.tools.exception.BaseException;
48

    
49
import com.vividsolutions.jts.geom.Coordinate;
50
import com.vividsolutions.jts.io.ParseException;
51

    
52
/**
53
 *  Converts a Well-Known Text string to a <code>Geometry</code>.
54
 * <p>
55
 *  The <code>WKTReader</code> allows
56
 *  extracting <code>Geometry</code> objects from either input streams or
57
 *  internal strings. This allows it to function as a parser to read <code>Geometry</code>
58
 *  objects from text blocks embedded in other data formats (e.g. XML). <P>
59
 * <p>
60
 * The Well-known
61
 *  Text format is defined in the <A HREF="http://www.opengis.org/techno/specs.htm">
62
 *  OpenGIS Simple Features Specification for SQL</A> . <P>
63
 * <p>
64
 *  <B>Note: </B> There is an inconsistency in the SFS. The WKT grammar states
65
 *  that <code>MultiPoints</code> are represented by <code>MULTIPOINT ( ( x y), (x y) )</code>
66
 *  , but the examples show <code>MultiPoint</code>s as <code>MULTIPOINT ( x y, x y )</code>
67
 *  . Other implementations follow the latter syntax, so JTS will adopt it as
68
 *  well.
69
 *
70
 *  A <code>WKTReader</code> is parameterized by a <code>GeometryFactory</code>
71
 *  , to allow it to create <code>Geometry</code> objects of the appropriate
72
 *  implementation. In particular, the <code>GeometryFactory</code> will
73
 *  determine the <code>PrecisionModel</code> and <code>SRID</code> that is
74
 *  used. <P>
75
 *
76
 *  The <code>WKTReader</code> will convert the input numbers to the precise
77
 *  internal representation.
78
 *
79
 *  Reads non-standard "LINEARRING" tags.
80
 *
81
 *@version 1.5
82
 */
83
public class WKTParser {
84

    
85
        private GeometryManager manager = GeometryLocator.getGeometryManager();
86
        /**
87
         * Creates a WKTReader that creates objects using a basic GeometryFactory.
88
         */
89
        public WKTParser() {
90
        }
91

    
92

    
93

    
94
        /**
95
         * Converts a Well-known Text representation to a <code>Geometry</code>.
96
         * 
97
         * @param wellKnownText
98
         *            one or more <Geometry Tagged Text>strings (see the OpenGIS
99
         *            Simple Features Specification) separated by whitespace
100
         * @return a <code>Geometry</code> specified by <code>wellKnownText</code>
101
         * @throws ParseException
102
         *             if a parsing problem occurs
103
         */
104
        public Geometry read(String wellKnownText) throws ParseException {
105
                StringReader reader = new StringReader(wellKnownText);
106
                try {
107
                        return read(reader);
108
                }
109
                finally {
110
                        reader.close();
111
                }
112
        }
113

    
114
        /**
115
         *  Converts a Well-known Text representation to a <code>Geometry</code>.
116
         *
117
         *@param  reader           a Reader which will return a <Geometry Tagged Text>
118
         *      string (see the OpenGIS Simple Features Specification)
119
         *@return                  a <code>Geometry</code> read from <code>reader</code>
120
         *@throws  ParseException  if a parsing problem occurs
121
         */
122
        public Geometry read(Reader reader) throws ParseException {
123
                StreamTokenizer tokenizer = new StreamTokenizer(reader);
124
                try {
125
                        return readGeometryTaggedText(tokenizer);
126
                }
127
                catch (IOException e) {
128
                        throw new ParseException(e.toString());
129
                } catch (CreateGeometryException e) {
130
                        throw new ParseException(e.toString());
131
                }
132
        }
133

    
134
        /**
135
         *  Returns the next array of <code>Coordinate</code>s in the stream.
136
         *
137
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
138
         *      format. The next element returned by the stream should be "(" (the
139
         *      beginning of "(x1 y1, x2 y2, ..., xn yn)") or "EMPTY".
140
         *@return                  the next array of <code>Coordinate</code>s in the
141
         *      stream, or an empty array if "EMPTY" is the next element returned by
142
         *      the stream.
143
         *@throws  IOException     if an I/O error occurs
144
         *@throws  ParseException  if an unexpected token was encountered
145
         */
146
        private Coordinate[] getCoordinates(StreamTokenizer tokenizer)
147
        throws IOException, ParseException
148
        {
149
                String nextToken = getNextEmptyOrOpener(tokenizer);
150
                if (nextToken.equals("EMPTY")) {
151
                        return new Coordinate[]{};
152
                }
153
                ArrayList coordinates = new ArrayList();
154
                coordinates.add(getPreciseCoordinate(tokenizer));
155
                nextToken = getNextCloserOrComma(tokenizer);
156
                while (nextToken.equals(",")) {
157
                        coordinates.add(getPreciseCoordinate(tokenizer));
158
                        nextToken = getNextCloserOrComma(tokenizer);
159
                }
160
                Coordinate[] array = new Coordinate[coordinates.size()];
161
                return (Coordinate[]) coordinates.toArray(array);
162
        }
163

    
164
        private Coordinate getPreciseCoordinate(StreamTokenizer tokenizer)
165
        throws IOException, ParseException
166
        {
167
                Coordinate coord = new Coordinate();
168
                coord.x = getNextNumber(tokenizer);
169
                coord.y = getNextNumber(tokenizer);
170
                if (isNumberNext(tokenizer)) {
171
                        coord.z = getNextNumber(tokenizer);
172
                }
173
                return coord;
174
        }
175
        private boolean isNumberNext(StreamTokenizer tokenizer) throws IOException {
176
                try {
177
                        return tokenizer.nextToken() == StreamTokenizer.TT_NUMBER;
178
                }
179
                finally {
180
                        tokenizer.pushBack();
181
                }
182
        }
183
        /**
184
         *  Returns the next number in the stream.
185
         *
186
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
187
         *      format. The next token must be a number.
188
         *@return                  the next number in the stream
189
         *@throws  ParseException  if the next token is not a number
190
         *@throws  IOException     if an I/O error occurs
191
         */
192
        private double getNextNumber(StreamTokenizer tokenizer) throws IOException,
193
        ParseException {
194
                int type = tokenizer.nextToken();
195
                switch (type) {
196
                case StreamTokenizer.TT_EOF:
197
                        throw new ParseException("Expected number but encountered end of stream");
198
                case StreamTokenizer.TT_EOL:
199
                        throw new ParseException("Expected number but encountered end of line");
200
                case StreamTokenizer.TT_NUMBER:
201
                        return tokenizer.nval;
202
                case StreamTokenizer.TT_WORD:
203
                        throw new ParseException("Expected number but encountered word: " +
204
                                        tokenizer.sval);
205
                case '(':
206
                        throw new ParseException("Expected number but encountered '('");
207
                case ')':
208
                        throw new ParseException("Expected number but encountered ')'");
209
                case ',':
210
                        throw new ParseException("Expected number but encountered ','");
211
                }
212
                return 0;
213
        }
214

    
215
        /**
216
         *  Returns the next "EMPTY" or "(" in the stream as uppercase text.
217
         *
218
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
219
         *      format. The next token must be "EMPTY" or "(".
220
         *@return                  the next "EMPTY" or "(" in the stream as uppercase
221
         *      text.
222
         *@throws  ParseException  if the next token is not "EMPTY" or "("
223
         *@throws  IOException     if an I/O error occurs
224
         */
225
        private String getNextEmptyOrOpener(StreamTokenizer tokenizer) throws IOException, ParseException {
226
                String nextWord = getNextWord(tokenizer);
227
                if (nextWord.equals("EMPTY") || nextWord.equals("(")) {
228
                        return nextWord;
229
                }
230
                throw new ParseException("Expected 'EMPTY' or '(' but encountered '" +
231
                                nextWord + "'");
232
        }
233

    
234
        /**
235
         *  Returns the next ")" or "," in the stream.
236
         *
237
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
238
         *      format. The next token must be ")" or ",".
239
         *@return                  the next ")" or "," in the stream
240
         *@throws  ParseException  if the next token is not ")" or ","
241
         *@throws  IOException     if an I/O error occurs
242
         */
243
        private String getNextCloserOrComma(StreamTokenizer tokenizer) throws IOException, ParseException {
244
                String nextWord = getNextWord(tokenizer);
245
                if (nextWord.equals(",") || nextWord.equals(")")) {
246
                        return nextWord;
247
                }
248
                throw new ParseException("Expected ')' or ',' but encountered '" + nextWord
249
                                + "'");
250
        }
251

    
252
        /**
253
         *  Returns the next ")" in the stream.
254
         *
255
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
256
         *      format. The next token must be ")".
257
         *@return                  the next ")" in the stream
258
         *@throws  ParseException  if the next token is not ")"
259
         *@throws  IOException     if an I/O error occurs
260
         */
261
        private String getNextCloser(StreamTokenizer tokenizer) throws IOException, ParseException {
262
                String nextWord = getNextWord(tokenizer);
263
                if (nextWord.equals(")")) {
264
                        return nextWord;
265
                }
266
                throw new ParseException("Expected ')' but encountered '" + nextWord + "'");
267
        }
268

    
269
        /**
270
         *  Returns the next word in the stream as uppercase text.
271
         *
272
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
273
         *      format. The next token must be a word.
274
         *@return                  the next word in the stream as uppercase text
275
         *@throws  ParseException  if the next token is not a word
276
         *@throws  IOException     if an I/O error occurs
277
         */
278
        private String getNextWord(StreamTokenizer tokenizer) throws IOException, ParseException {
279
                int type = tokenizer.nextToken();
280
                switch (type) {
281
                case StreamTokenizer.TT_EOF:
282
                        throw new ParseException("Expected word but encountered end of stream");
283
                case StreamTokenizer.TT_EOL:
284
                        throw new ParseException("Expected word but encountered end of line");
285
                case StreamTokenizer.TT_NUMBER:
286
                        throw new ParseException("Expected word but encountered number: " +
287
                                        tokenizer.nval);
288
                case StreamTokenizer.TT_WORD:
289
                        return tokenizer.sval.toUpperCase();
290
                case '(':
291
                        return "(";
292
                case ')':
293
                        return ")";
294
                case ',':
295
                        return ",";
296
                }
297
                // Assert.shouldNeverReachHere("Encountered unexpected StreamTokenizer type: " + type);
298
                return null;
299
        }
300

    
301
        /**
302
         *  Creates a <code>Geometry</code> using the next token in the stream.
303
         *
304
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
305
         *      format. The next tokens must form a &lt;Geometry Tagged Text&gt;.
306
         *@return                  a <code>Geometry</code> specified by the next token
307
         *      in the stream
308
         *@throws  ParseException  if the coordinates used to create a <code>Polygon</code>
309
         *      shell and holes do not form closed linestrings, or if an unexpected
310
         *      token was encountered
311
         *@throws  IOException     if an I/O error occurs
312
         * @throws CreateGeometryException 
313
         */
314
        private Geometry readGeometryTaggedText(StreamTokenizer tokenizer) throws IOException, ParseException, CreateGeometryException{
315
                String type = getNextWord(tokenizer);
316
                if (type.equals("POINT")) {
317
                        return readPointText(tokenizer);
318
                }
319
                else if (type.equals("LINESTRING")) {
320
                        return readLineStringText(tokenizer);
321
                }
322
                else if (type.equals("LINEARRING")) {
323
                        return readLinearRingText(tokenizer);
324
                }
325
                else if (type.equals("POLYGON")) {
326
                        return readPolygonText(tokenizer);
327
                }
328
                else if (type.equals("MULTIPOINT")) {
329
                        return readMultiPointText(tokenizer);
330
                }
331
                else if (type.equals("MULTILINESTRING")) {
332
                        return readMultiLineStringText(tokenizer);
333
                }
334
                else if (type.equals("MULTIPOLYGON")) {
335
                        return readMultiPolygonText(tokenizer);
336
                }
337
                /* else if (type.equals("GEOMETRYCOLLECTION")) {
338
      return readGeometryCollectionText(tokenizer);
339
    } */
340
                System.err.println("Unknown type: " + type);
341
                throw new ParseException("Unknown type: " + type);
342
        }
343

    
344
        /**
345
         *  Creates a <code>Point</code> using the next token in the stream.
346
         *
347
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
348
         *      format. The next tokens must form a &lt;Point Text&gt;.
349
         *@return                  a <code>Point</code> specified by the next token in
350
         *      the stream
351
         *@throws  IOException     if an I/O error occurs
352
         *@throws  ParseException  if an unexpected token was encountered
353
         * @throws IllegalAccessException 
354
         * @throws InstantiationException 
355
         * @throws CreateGeometryException 
356
         */
357
        private Geometry readPointText(StreamTokenizer tokenizer) throws IOException, ParseException, CreateGeometryException {
358
                String nextToken = getNextEmptyOrOpener(tokenizer);
359
                if (nextToken.equals("EMPTY")) {
360
                        return null;
361
                }
362
                Coordinate c = getPreciseCoordinate(tokenizer);
363
                Point point = (Point)manager.create(TYPES.POINT, SUBTYPES.GEOM2D);
364
                point.setX(c.x);
365
                point.setY(c.y);
366
                getNextCloser(tokenizer);
367

    
368
                return point;
369
        }
370

    
371
        /**
372
         *  Creates a <code>LineString</code> using the next token in the stream.
373
         *
374
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
375
         *      format. The next tokens must form a &lt;LineString Text&gt;.
376
         *@return                  a <code>LineString</code> specified by the next
377
         *      token in the stream
378
         *@throws  IOException     if an I/O error occurs
379
         *@throws  ParseException  if an unexpected token was encountered
380
         * @throws CreateGeometryException 
381
         * @throws IllegalAccessException 
382
         * @throws InstantiationException 
383
         */
384
        private Curve readLineStringText(StreamTokenizer tokenizer) throws IOException, ParseException, CreateGeometryException {
385
                Coordinate[] arrayC = getCoordinates(tokenizer);
386
                GeneralPathX gp = new GeneralPathX();
387
                gp.moveTo(arrayC[0].x,arrayC[0].y);
388
                for (int i=1;i < arrayC.length; i++)
389
                {
390
                        gp.lineTo(arrayC[i].x, arrayC[i].y);
391
                }
392
        // IJM: Cambiado Surface por Curve 
393
        Curve curve = (Curve) manager.create(TYPES.CURVE, SUBTYPES.GEOM2D); 
394
        curve.setGeneralPath(gp); 
395
        return curve; 
396
        }
397

    
398
        /**
399
         *  Creates a <code>LinearRing</code> using the next token in the stream.
400
         *
401
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
402
         *      format. The next tokens must form a &lt;LineString Text&gt;.
403
         *@return                  a <code>LinearRing</code> specified by the next
404
         *      token in the stream
405
         *@throws  IOException     if an I/O error occurs
406
         *@throws  ParseException  if the coordinates used to create the <code>LinearRing</code>
407
         *      do not form a closed linestring, or if an unexpected token was
408
         *      encountered
409
         * @throws IllegalAccessException 
410
         * @throws InstantiationException 
411
         * @throws CreateGeometryException 
412
         */
413
        private Surface readLinearRingText(StreamTokenizer tokenizer)
414
        throws IOException, ParseException, CreateGeometryException
415
        {
416
                Coordinate[] arrayC = getCoordinates(tokenizer);
417
                GeneralPathX gp = new GeneralPathX();
418
                gp.moveTo(arrayC[0].x, arrayC[0].y);
419
                for (int i=1;i < arrayC.length; i++)
420
                {
421
                        gp.lineTo(arrayC[i].x, arrayC[i].y);
422
                }
423
                Surface surface = (Surface)manager.create(TYPES.SURFACE, SUBTYPES.GEOM2D);
424
                surface.setGeneralPath(gp);
425
                return surface;
426
        }
427

    
428
        /**
429
         *  Creates a <code>MultiPoint</code> using the next token in the stream.
430
         *
431
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
432
         *      format. The next tokens must form a &lt;MultiPoint Text&gt;.
433
         *@return                  a <code>MultiPoint</code> specified by the next
434
         *      token in the stream
435
         *@throws  IOException     if an I/O error occurs
436
         *@throws  ParseException  if an unexpected token was encountered
437
         * @throws IllegalAccessException 
438
         * @throws InstantiationException 
439
         * @throws CreateGeometryException 
440
         */
441
        private Geometry readMultiPointText(StreamTokenizer tokenizer) throws IOException, ParseException, CreateGeometryException {
442
                Coordinate[] coords = getCoordinates(tokenizer);
443
                MultiPoint multiPoint = (MultiPoint)manager.create(TYPES.MULTIPOINT, SUBTYPES.GEOM2D);
444
                for (int i=0; i < coords.length; i++)
445
                {
446
                        Point point = (Point)manager.create(TYPES.POINT, SUBTYPES.GEOM2D);
447
                        point.setX(coords[i].x);
448
                        point.setY(coords[i].y);
449
                        multiPoint.addPoint(point);
450
                }   
451

    
452
                return multiPoint;
453
        }
454

    
455

    
456
        /**
457
         *  Creates a <code>Polygon</code> using the next token in the stream.
458
         *
459
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
460
         *      format. The next tokens must form a &lt;Polygon Text&gt;.
461
         *@return                  a <code>Polygon</code> specified by the next token
462
         *      in the stream
463
         *@throws  ParseException  if the coordinates used to create the <code>Polygon</code>
464
         *      shell and holes do not form closed linestrings, or if an unexpected
465
         *      token was encountered.
466
         *@throws  IOException     if an I/O error occurs
467
         * @throws IllegalAccessException 
468
         * @throws InstantiationException 
469
         * @throws CreateGeometryException 
470
         */
471
        private Surface readPolygonText(StreamTokenizer tokenizer) throws IOException, ParseException, CreateGeometryException {
472
                String nextToken = getNextEmptyOrOpener(tokenizer);
473
                if (nextToken.equals("EMPTY")) {
474
                        return null;
475
                }
476

    
477
                Surface shell = readLinearRingText(tokenizer);
478
                Surface hole = null;
479
                nextToken = getNextCloserOrComma(tokenizer);
480
                
481
                try {
482
                        while (nextToken.equals(",")) {
483
                                hole = readLinearRingText(tokenizer);
484
                                if (shell.contains(hole)) {
485
                                        shell = (Surface) shell.difference(hole);
486
                                } else {
487
                                        throw new CreateGeometryException(
488
                                                        Geometry.TYPES.SURFACE,
489
                                                        Geometry.SUBTYPES.GEOM2D,
490
                                                        new Exception("Hole not in shell."));
491
                                        
492
                                }
493
                                nextToken = getNextCloserOrComma(tokenizer);
494
                        }
495
                } catch (BaseException ge) {
496
                        throw new CreateGeometryException(
497
                                        Geometry.TYPES.SURFACE,
498
                                        Geometry.SUBTYPES.GEOM2D, ge);
499
                }
500
                return shell; //geometryFactory.createPolygon(shell, (LinearRing[]) holes.toArray(array));
501
        }
502

    
503
        /**
504
         *  Creates a <code>MultiLineString</code> using the next token in the stream.
505
         *
506
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
507
         *      format. The next tokens must form a &lt;MultiLineString Text&gt;.
508
         *@return                  a <code>MultiLineString</code> specified by the
509
         *      next token in the stream
510
         *@throws  IOException     if an I/O error occurs
511
         *@throws  ParseException  if an unexpected token was encountered
512
         * @throws IllegalAccessException 
513
         * @throws InstantiationException 
514
         * @throws CreateGeometryException 
515
         */
516
        private Geometry readMultiLineStringText(StreamTokenizer tokenizer) throws IOException, ParseException, CreateGeometryException {
517
                // TODO: HACER ESTO BIEN, CON UN GENERAL PATH
518
                String nextToken = getNextEmptyOrOpener(tokenizer);
519
                if (nextToken.equals("EMPTY")) {
520
                        return null;
521
                }
522
                
523
                MultiCurve mcu = manager.createMultiCurve(SUBTYPES.GEOM2D);
524
                Curve lineString = readLineStringText(tokenizer);
525
                mcu.addCurve(lineString);
526
                
527
                nextToken = getNextCloserOrComma(tokenizer);
528
                while (nextToken.equals(",")) {
529
                        lineString = readLineStringText(tokenizer);
530
                        mcu.addCurve(lineString);
531
                        nextToken = getNextCloserOrComma(tokenizer);
532
                } 
533
                // LineString[] array = new LineString[lineStrings.size()];
534
                return mcu; // geometryFactory.createMultiLineString((LineString[]) lineStrings.toArray(array));
535
        }
536

    
537
        /**
538
         *  Creates a <code>MultiPolygon</code> using the next token in the stream.
539
         *
540
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
541
         *      format. The next tokens must form a &lt;MultiPolygon Text&gt;.
542
         *@return                  a <code>MultiPolygon</code> specified by the next
543
         *      token in the stream, or if if the coordinates used to create the
544
         *      <code>Polygon</code> shells and holes do not form closed linestrings.
545
         *@throws  IOException     if an I/O error occurs
546
         *@throws  ParseException  if an unexpected token was encountered
547
         * @throws CreateGeometryException 
548
         */
549
        // TODO:
550
        private Geometry readMultiPolygonText(StreamTokenizer tokenizer) throws IOException, ParseException, CreateGeometryException {
551
                String nextToken = getNextEmptyOrOpener(tokenizer);
552
                if (nextToken.equals("EMPTY")) {
553
                        return null;
554
                }
555
                
556
                MultiSurface msu = manager.createMultiSurface(SUBTYPES.GEOM2D);
557
                Surface polygon = readPolygonText(tokenizer);
558
                msu.addSurface(polygon);
559
                
560
                nextToken = getNextCloserOrComma(tokenizer);
561
                while (nextToken.equals(",")) {
562
                        polygon = readPolygonText(tokenizer);
563
                        msu.addSurface(polygon);
564
                        nextToken = getNextCloserOrComma(tokenizer);
565
                }
566
                // Polygon[] array = new Polygon[polygons.size()];
567
                return msu; //geometryFactory.createMultiPolygon((Polygon[]) polygons.toArray(array));
568
        } 
569

    
570
        /**
571
         *  Creates a <code>GeometryCollection</code> using the next token in the
572
         *  stream.
573
         *
574
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
575
         *      format. The next tokens must form a &lt;GeometryCollection Text&gt;.
576
         *@return                  a <code>GeometryCollection</code> specified by the
577
         *      next token in the stream
578
         *@throws  ParseException  if the coordinates used to create a <code>Polygon</code>
579
         *      shell and holes do not form closed linestrings, or if an unexpected
580
         *      token was encountered
581
         *@throws  IOException     if an I/O error occurs
582
         */
583
        // TODO:
584
        /* private GeometryCollection readGeometryCollectionText(StreamTokenizer tokenizer) throws IOException, ParseException {
585
    String nextToken = getNextEmptyOrOpener(tokenizer);
586
    if (nextToken.equals("EMPTY")) {
587
      return geometryFactory.createGeometryCollection(new Geometry[]{});
588
    }
589
    ArrayList geometries = new ArrayList();
590
    Geometry geometry = readGeometryTaggedText(tokenizer);
591
    geometries.add(geometry);
592
    nextToken = getNextCloserOrComma(tokenizer);
593
    while (nextToken.equals(",")) {
594
      geometry = readGeometryTaggedText(tokenizer);
595
      geometries.add(geometry);
596
      nextToken = getNextCloserOrComma(tokenizer);
597
    }
598
    Geometry[] array = new Geometry[geometries.size()];
599
    return geometryFactory.createGeometryCollection((Geometry[]) geometries.toArray(array));
600
  } */
601
}
602