Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libFMap_geometries / src / org / gvsig / fmap / geom / operation / fromwkt / WKTParser.java @ 26872

History | View | Annotate | Download (20.7 KB)

1
package org.gvsig.fmap.geom.operation.fromwkt;
2

    
3
import java.io.IOException;
4
import java.io.Reader;
5
import java.io.StreamTokenizer;
6
import java.io.StringReader;
7
import java.util.ArrayList;
8

    
9
import org.gvsig.fmap.geom.Geometry;
10
import org.gvsig.fmap.geom.GeometryLocator;
11
import org.gvsig.fmap.geom.GeometryManager;
12
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
13
import org.gvsig.fmap.geom.Geometry.TYPES;
14
import org.gvsig.fmap.geom.aggregate.MultiPoint;
15
import org.gvsig.fmap.geom.primitive.GeneralPathX;
16
import org.gvsig.fmap.geom.primitive.Point;
17
import org.gvsig.fmap.geom.primitive.Surface;
18

    
19
import com.vividsolutions.jts.geom.Coordinate;
20
import com.vividsolutions.jts.io.ParseException;
21

    
22
/**
23
 *  Converts a Well-Known Text string to a <code>Geometry</code>.
24
 * <p>
25
 *  The <code>WKTReader</code> allows
26
 *  extracting <code>Geometry</code> objects from either input streams or
27
 *  internal strings. This allows it to function as a parser to read <code>Geometry</code>
28
 *  objects from text blocks embedded in other data formats (e.g. XML). <P>
29
 * <p>
30
 * The Well-known
31
 *  Text format is defined in the <A HREF="http://www.opengis.org/techno/specs.htm">
32
 *  OpenGIS Simple Features Specification for SQL</A> . <P>
33
 * <p>
34
 *  <B>Note: </B> There is an inconsistency in the SFS. The WKT grammar states
35
 *  that <code>MultiPoints</code> are represented by <code>MULTIPOINT ( ( x y), (x y) )</code>
36
 *  , but the examples show <code>MultiPoint</code>s as <code>MULTIPOINT ( x y, x y )</code>
37
 *  . Other implementations follow the latter syntax, so JTS will adopt it as
38
 *  well.
39
 *
40
 *  A <code>WKTReader</code> is parameterized by a <code>GeometryFactory</code>
41
 *  , to allow it to create <code>Geometry</code> objects of the appropriate
42
 *  implementation. In particular, the <code>GeometryFactory</code> will
43
 *  determine the <code>PrecisionModel</code> and <code>SRID</code> that is
44
 *  used. <P>
45
 *
46
 *  The <code>WKTReader</code> will convert the input numbers to the precise
47
 *  internal representation.
48
 *
49
 *  Reads non-standard "LINEARRING" tags.
50
 *
51
 *@version 1.5
52
 */
53
public class WKTParser {
54

    
55
        private GeometryManager manager = GeometryLocator.getGeometryManager();
56
        /**
57
         * Creates a WKTReader that creates objects using a basic GeometryFactory.
58
         */
59
        public WKTParser() {
60
        }
61

    
62

    
63

    
64
        /**
65
         * Converts a Well-known Text representation to a <code>Geometry</code>.
66
         * 
67
         * @param wellKnownText
68
         *            one or more <Geometry Tagged Text>strings (see the OpenGIS
69
         *            Simple Features Specification) separated by whitespace
70
         * @return a <code>Geometry</code> specified by <code>wellKnownText</code>
71
         * @throws ParseException
72
         *             if a parsing problem occurs
73
         */
74
        public Geometry read(String wellKnownText) throws ParseException {
75
                StringReader reader = new StringReader(wellKnownText);
76
                try {
77
                        return read(reader);
78
                }
79
                finally {
80
                        reader.close();
81
                }
82
        }
83

    
84
        /**
85
         *  Converts a Well-known Text representation to a <code>Geometry</code>.
86
         *
87
         *@param  reader           a Reader which will return a <Geometry Tagged Text>
88
         *      string (see the OpenGIS Simple Features Specification)
89
         *@return                  a <code>Geometry</code> read from <code>reader</code>
90
         *@throws  ParseException  if a parsing problem occurs
91
         */
92
        public Geometry read(Reader reader) throws ParseException {
93
                StreamTokenizer tokenizer = new StreamTokenizer(reader);
94
                try {
95
                        return readGeometryTaggedText(tokenizer);
96
                }
97
                catch (IOException e) {
98
                        throw new ParseException(e.toString());
99
                } catch (InstantiationException e) {
100
                        throw new ParseException(e.toString());
101
                } catch (IllegalAccessException e) {
102
                        throw new ParseException(e.toString());
103
                }
104
        }
105

    
106
        /**
107
         *  Returns the next array of <code>Coordinate</code>s in the stream.
108
         *
109
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
110
         *      format. The next element returned by the stream should be "(" (the
111
         *      beginning of "(x1 y1, x2 y2, ..., xn yn)") or "EMPTY".
112
         *@return                  the next array of <code>Coordinate</code>s in the
113
         *      stream, or an empty array if "EMPTY" is the next element returned by
114
         *      the stream.
115
         *@throws  IOException     if an I/O error occurs
116
         *@throws  ParseException  if an unexpected token was encountered
117
         */
118
        private Coordinate[] getCoordinates(StreamTokenizer tokenizer)
119
        throws IOException, ParseException
120
        {
121
                String nextToken = getNextEmptyOrOpener(tokenizer);
122
                if (nextToken.equals("EMPTY")) {
123
                        return new Coordinate[]{};
124
                }
125
                ArrayList coordinates = new ArrayList();
126
                coordinates.add(getPreciseCoordinate(tokenizer));
127
                nextToken = getNextCloserOrComma(tokenizer);
128
                while (nextToken.equals(",")) {
129
                        coordinates.add(getPreciseCoordinate(tokenizer));
130
                        nextToken = getNextCloserOrComma(tokenizer);
131
                }
132
                Coordinate[] array = new Coordinate[coordinates.size()];
133
                return (Coordinate[]) coordinates.toArray(array);
134
        }
135

    
136
        private Coordinate getPreciseCoordinate(StreamTokenizer tokenizer)
137
        throws IOException, ParseException
138
        {
139
                Coordinate coord = new Coordinate();
140
                coord.x = getNextNumber(tokenizer);
141
                coord.y = getNextNumber(tokenizer);
142
                if (isNumberNext(tokenizer)) {
143
                        coord.z = getNextNumber(tokenizer);
144
                }
145
                return coord;
146
        }
147
        private boolean isNumberNext(StreamTokenizer tokenizer) throws IOException {
148
                try {
149
                        return tokenizer.nextToken() == StreamTokenizer.TT_NUMBER;
150
                }
151
                finally {
152
                        tokenizer.pushBack();
153
                }
154
        }
155
        /**
156
         *  Returns the next number in the stream.
157
         *
158
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
159
         *      format. The next token must be a number.
160
         *@return                  the next number in the stream
161
         *@throws  ParseException  if the next token is not a number
162
         *@throws  IOException     if an I/O error occurs
163
         */
164
        private double getNextNumber(StreamTokenizer tokenizer) throws IOException,
165
        ParseException {
166
                int type = tokenizer.nextToken();
167
                switch (type) {
168
                case StreamTokenizer.TT_EOF:
169
                        throw new ParseException("Expected number but encountered end of stream");
170
                case StreamTokenizer.TT_EOL:
171
                        throw new ParseException("Expected number but encountered end of line");
172
                case StreamTokenizer.TT_NUMBER:
173
                        return tokenizer.nval;
174
                case StreamTokenizer.TT_WORD:
175
                        throw new ParseException("Expected number but encountered word: " +
176
                                        tokenizer.sval);
177
                case '(':
178
                        throw new ParseException("Expected number but encountered '('");
179
                case ')':
180
                        throw new ParseException("Expected number but encountered ')'");
181
                case ',':
182
                        throw new ParseException("Expected number but encountered ','");
183
                }
184
                return 0;
185
        }
186

    
187
        /**
188
         *  Returns the next "EMPTY" or "(" in the stream as uppercase text.
189
         *
190
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
191
         *      format. The next token must be "EMPTY" or "(".
192
         *@return                  the next "EMPTY" or "(" in the stream as uppercase
193
         *      text.
194
         *@throws  ParseException  if the next token is not "EMPTY" or "("
195
         *@throws  IOException     if an I/O error occurs
196
         */
197
        private String getNextEmptyOrOpener(StreamTokenizer tokenizer) throws IOException, ParseException {
198
                String nextWord = getNextWord(tokenizer);
199
                if (nextWord.equals("EMPTY") || nextWord.equals("(")) {
200
                        return nextWord;
201
                }
202
                throw new ParseException("Expected 'EMPTY' or '(' but encountered '" +
203
                                nextWord + "'");
204
        }
205

    
206
        /**
207
         *  Returns the next ")" or "," in the stream.
208
         *
209
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
210
         *      format. The next token must be ")" or ",".
211
         *@return                  the next ")" or "," in the stream
212
         *@throws  ParseException  if the next token is not ")" or ","
213
         *@throws  IOException     if an I/O error occurs
214
         */
215
        private String getNextCloserOrComma(StreamTokenizer tokenizer) throws IOException, ParseException {
216
                String nextWord = getNextWord(tokenizer);
217
                if (nextWord.equals(",") || nextWord.equals(")")) {
218
                        return nextWord;
219
                }
220
                throw new ParseException("Expected ')' or ',' but encountered '" + nextWord
221
                                + "'");
222
        }
223

    
224
        /**
225
         *  Returns the next ")" in the stream.
226
         *
227
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
228
         *      format. The next token must be ")".
229
         *@return                  the next ")" in the stream
230
         *@throws  ParseException  if the next token is not ")"
231
         *@throws  IOException     if an I/O error occurs
232
         */
233
        private String getNextCloser(StreamTokenizer tokenizer) throws IOException, ParseException {
234
                String nextWord = getNextWord(tokenizer);
235
                if (nextWord.equals(")")) {
236
                        return nextWord;
237
                }
238
                throw new ParseException("Expected ')' but encountered '" + nextWord + "'");
239
        }
240

    
241
        /**
242
         *  Returns the next word in the stream as uppercase text.
243
         *
244
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
245
         *      format. The next token must be a word.
246
         *@return                  the next word in the stream as uppercase text
247
         *@throws  ParseException  if the next token is not a word
248
         *@throws  IOException     if an I/O error occurs
249
         */
250
        private String getNextWord(StreamTokenizer tokenizer) throws IOException, ParseException {
251
                int type = tokenizer.nextToken();
252
                switch (type) {
253
                case StreamTokenizer.TT_EOF:
254
                        throw new ParseException("Expected word but encountered end of stream");
255
                case StreamTokenizer.TT_EOL:
256
                        throw new ParseException("Expected word but encountered end of line");
257
                case StreamTokenizer.TT_NUMBER:
258
                        throw new ParseException("Expected word but encountered number: " +
259
                                        tokenizer.nval);
260
                case StreamTokenizer.TT_WORD:
261
                        return tokenizer.sval.toUpperCase();
262
                case '(':
263
                        return "(";
264
                case ')':
265
                        return ")";
266
                case ',':
267
                        return ",";
268
                }
269
                // Assert.shouldNeverReachHere("Encountered unexpected StreamTokenizer type: " + type);
270
                return null;
271
        }
272

    
273
        /**
274
         *  Creates a <code>Geometry</code> using the next token in the stream.
275
         *
276
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
277
         *      format. The next tokens must form a &lt;Geometry Tagged Text&gt;.
278
         *@return                  a <code>Geometry</code> specified by the next token
279
         *      in the stream
280
         *@throws  ParseException  if the coordinates used to create a <code>Polygon</code>
281
         *      shell and holes do not form closed linestrings, or if an unexpected
282
         *      token was encountered
283
         *@throws  IOException     if an I/O error occurs
284
         * @throws IllegalAccessException 
285
         * @throws InstantiationException 
286
         */
287
        private Geometry readGeometryTaggedText(StreamTokenizer tokenizer) throws IOException, ParseException, InstantiationException, IllegalAccessException {
288
                String type = getNextWord(tokenizer);
289
                if (type.equals("POINT")) {
290
                        return readPointText(tokenizer);
291
                }
292
                else if (type.equals("LINESTRING")) {
293
                        return readLineStringText(tokenizer);
294
                }
295
                else if (type.equals("LINEARRING")) {
296
                        return readLinearRingText(tokenizer);
297
                }
298
                else if (type.equals("POLYGON")) {
299
                        return readPolygonText(tokenizer);
300
                }
301
                else if (type.equals("MULTIPOINT")) {
302
                        return readMultiPointText(tokenizer);
303
                }
304
                else if (type.equals("MULTILINESTRING")) {
305
                        return readMultiLineStringText(tokenizer);
306
                }
307
                else if (type.equals("MULTIPOLYGON")) {
308
                        return readMultiPolygonText(tokenizer);
309
                }
310
                /* else if (type.equals("GEOMETRYCOLLECTION")) {
311
      return readGeometryCollectionText(tokenizer);
312
    } */
313
                System.err.println("Unknown type: " + type);
314
                throw new ParseException("Unknown type: " + type);
315
        }
316

    
317
        /**
318
         *  Creates a <code>Point</code> using the next token in the stream.
319
         *
320
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
321
         *      format. The next tokens must form a &lt;Point Text&gt;.
322
         *@return                  a <code>Point</code> specified by the next token in
323
         *      the stream
324
         *@throws  IOException     if an I/O error occurs
325
         *@throws  ParseException  if an unexpected token was encountered
326
         * @throws IllegalAccessException 
327
         * @throws InstantiationException 
328
         */
329
        private Geometry readPointText(StreamTokenizer tokenizer) throws IOException, ParseException, InstantiationException, IllegalAccessException {
330
                String nextToken = getNextEmptyOrOpener(tokenizer);
331
                if (nextToken.equals("EMPTY")) {
332
                        return null;
333
                }
334
                Coordinate c = getPreciseCoordinate(tokenizer);
335
                Point point = (Point)manager.create(TYPES.POINT, SUBTYPES.GEOM2D);
336
                point.setX(c.x);
337
                point.setY(c.y);
338
                getNextCloser(tokenizer);
339

    
340
                return point;
341
        }
342

    
343
        /**
344
         *  Creates a <code>LineString</code> using the next token in the stream.
345
         *
346
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
347
         *      format. The next tokens must form a &lt;LineString Text&gt;.
348
         *@return                  a <code>LineString</code> specified by the next
349
         *      token in the stream
350
         *@throws  IOException     if an I/O error occurs
351
         *@throws  ParseException  if an unexpected token was encountered
352
         * @throws IllegalAccessException 
353
         * @throws InstantiationException 
354
         */
355
        private Geometry readLineStringText(StreamTokenizer tokenizer) throws IOException, ParseException, InstantiationException, IllegalAccessException {
356
                Coordinate[] arrayC = getCoordinates(tokenizer);
357
                GeneralPathX gp = new GeneralPathX();
358
                gp.moveTo(arrayC[0].x,arrayC[0].y);
359
                for (int i=1;i < arrayC.length; i++)
360
                {
361
                        gp.lineTo(arrayC[i].x, arrayC[i].y);
362
                }
363
                Surface surface = (Surface)manager.create(TYPES.SURFACE, SUBTYPES.GEOM2D);
364
                surface.setGeneralPath(gp);
365
                return surface;
366
        }
367

    
368
        /**
369
         *  Creates a <code>LinearRing</code> using the next token in the stream.
370
         *
371
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
372
         *      format. The next tokens must form a &lt;LineString Text&gt;.
373
         *@return                  a <code>LinearRing</code> specified by the next
374
         *      token in the stream
375
         *@throws  IOException     if an I/O error occurs
376
         *@throws  ParseException  if the coordinates used to create the <code>LinearRing</code>
377
         *      do not form a closed linestring, or if an unexpected token was
378
         *      encountered
379
         * @throws IllegalAccessException 
380
         * @throws InstantiationException 
381
         */
382
        private Geometry readLinearRingText(StreamTokenizer tokenizer)
383
        throws IOException, ParseException, InstantiationException, IllegalAccessException
384
        {
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
                Surface surface = (Surface)manager.create(TYPES.SURFACE, SUBTYPES.GEOM2D);
393
                surface.setGeneralPath(gp);
394
                return surface;
395
        }
396

    
397
        /**
398
         *  Creates a <code>MultiPoint</code> using the next token in the stream.
399
         *
400
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
401
         *      format. The next tokens must form a &lt;MultiPoint Text&gt;.
402
         *@return                  a <code>MultiPoint</code> specified by the next
403
         *      token in the stream
404
         *@throws  IOException     if an I/O error occurs
405
         *@throws  ParseException  if an unexpected token was encountered
406
         * @throws IllegalAccessException 
407
         * @throws InstantiationException 
408
         */
409
        private Geometry readMultiPointText(StreamTokenizer tokenizer) throws IOException, ParseException, InstantiationException, IllegalAccessException {
410
                Coordinate[] coords = getCoordinates(tokenizer);
411
                MultiPoint multiPoint = (MultiPoint)manager.create(TYPES.MULTIPOINT, SUBTYPES.GEOM2D);
412
                for (int i=0; i < coords.length; i++)
413
                {
414
                        Point point = (Point)manager.create(TYPES.POINT, SUBTYPES.GEOM2D);
415
                        point.setX(coords[i].x);
416
                        point.setY(coords[i].y);
417
                        multiPoint.addPoint(point);
418
                }   
419

    
420
                return multiPoint;
421
        }
422

    
423

    
424
        /**
425
         *  Creates a <code>Polygon</code> using the next token in the stream.
426
         *
427
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
428
         *      format. The next tokens must form a &lt;Polygon Text&gt;.
429
         *@return                  a <code>Polygon</code> specified by the next token
430
         *      in the stream
431
         *@throws  ParseException  if the coordinates used to create the <code>Polygon</code>
432
         *      shell and holes do not form closed linestrings, or if an unexpected
433
         *      token was encountered.
434
         *@throws  IOException     if an I/O error occurs
435
         * @throws IllegalAccessException 
436
         * @throws InstantiationException 
437
         */
438
        private Geometry readPolygonText(StreamTokenizer tokenizer) throws IOException, ParseException, InstantiationException, IllegalAccessException {
439
                String nextToken = getNextEmptyOrOpener(tokenizer);
440
                if (nextToken.equals("EMPTY")) {
441
                        return null;
442
                }
443
                ArrayList holes = new ArrayList();
444
                Geometry shell = readLinearRingText(tokenizer);
445
                nextToken = getNextCloserOrComma(tokenizer);
446
                while (nextToken.equals(",")) {
447
                        Geometry hole = readLinearRingText(tokenizer);
448
                        holes.add(hole);
449
                        nextToken = getNextCloserOrComma(tokenizer);
450
                }
451
                // LinearRing[] array = new LinearRing[holes.size()];
452
                return shell; //geometryFactory.createPolygon(shell, (LinearRing[]) holes.toArray(array));
453
        }
454

    
455
        /**
456
         *  Creates a <code>MultiLineString</code> using the next token in the stream.
457
         *
458
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
459
         *      format. The next tokens must form a &lt;MultiLineString Text&gt;.
460
         *@return                  a <code>MultiLineString</code> specified by the
461
         *      next token in the stream
462
         *@throws  IOException     if an I/O error occurs
463
         *@throws  ParseException  if an unexpected token was encountered
464
         * @throws IllegalAccessException 
465
         * @throws InstantiationException 
466
         */
467
        private Geometry readMultiLineStringText(StreamTokenizer tokenizer) throws IOException, ParseException, InstantiationException, IllegalAccessException {
468
                // TODO: HACER ESTO BIEN, CON UN GENERAL PATH
469
                String nextToken = getNextEmptyOrOpener(tokenizer);
470
                if (nextToken.equals("EMPTY")) {
471
                        return null;
472
                }
473
                ArrayList lineStrings = new ArrayList();
474
                Geometry lineString = readLineStringText(tokenizer);
475
                lineStrings.add(lineString);
476
                nextToken = getNextCloserOrComma(tokenizer);
477
                while (nextToken.equals(",")) {
478
                        lineString = readLineStringText(tokenizer);
479
                        lineStrings.add(lineString);
480
                        nextToken = getNextCloserOrComma(tokenizer);
481
                } 
482
                // LineString[] array = new LineString[lineStrings.size()];
483
                return lineString; // geometryFactory.createMultiLineString((LineString[]) lineStrings.toArray(array));
484
        }
485

    
486
        /**
487
         *  Creates a <code>MultiPolygon</code> using the next token in the stream.
488
         *
489
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
490
         *      format. The next tokens must form a &lt;MultiPolygon Text&gt;.
491
         *@return                  a <code>MultiPolygon</code> specified by the next
492
         *      token in the stream, or if if the coordinates used to create the
493
         *      <code>Polygon</code> shells and holes do not form closed linestrings.
494
         *@throws  IOException     if an I/O error occurs
495
         *@throws  ParseException  if an unexpected token was encountered
496
         * @throws IllegalAccessException 
497
         * @throws InstantiationException 
498
         */
499
        // TODO:
500
        private Geometry readMultiPolygonText(StreamTokenizer tokenizer) throws IOException, ParseException, InstantiationException, IllegalAccessException {
501
                String nextToken = getNextEmptyOrOpener(tokenizer);
502
                if (nextToken.equals("EMPTY")) {
503
                        return null;
504
                }
505
                ArrayList polygons = new ArrayList();
506
                Geometry polygon = readPolygonText(tokenizer);
507
                /* polygons.add(polygon);
508
    nextToken = getNextCloserOrComma(tokenizer);
509
    while (nextToken.equals(",")) {
510
      polygon = readPolygonText(tokenizer);
511
      polygons.add(polygon);
512
      nextToken = getNextCloserOrComma(tokenizer);
513
    } */
514
                // Polygon[] array = new Polygon[polygons.size()];
515
                return polygon; //geometryFactory.createMultiPolygon((Polygon[]) polygons.toArray(array));
516
        } 
517

    
518
        /**
519
         *  Creates a <code>GeometryCollection</code> using the next token in the
520
         *  stream.
521
         *
522
         *@param  tokenizer        tokenizer over a stream of text in Well-known Text
523
         *      format. The next tokens must form a &lt;GeometryCollection Text&gt;.
524
         *@return                  a <code>GeometryCollection</code> specified by the
525
         *      next token in the stream
526
         *@throws  ParseException  if the coordinates used to create a <code>Polygon</code>
527
         *      shell and holes do not form closed linestrings, or if an unexpected
528
         *      token was encountered
529
         *@throws  IOException     if an I/O error occurs
530
         */
531
        // TODO:
532
        /* private GeometryCollection readGeometryCollectionText(StreamTokenizer tokenizer) throws IOException, ParseException {
533
    String nextToken = getNextEmptyOrOpener(tokenizer);
534
    if (nextToken.equals("EMPTY")) {
535
      return geometryFactory.createGeometryCollection(new Geometry[]{});
536
    }
537
    ArrayList geometries = new ArrayList();
538
    Geometry geometry = readGeometryTaggedText(tokenizer);
539
    geometries.add(geometry);
540
    nextToken = getNextCloserOrComma(tokenizer);
541
    while (nextToken.equals(",")) {
542
      geometry = readGeometryTaggedText(tokenizer);
543
      geometries.add(geometry);
544
      nextToken = getNextCloserOrComma(tokenizer);
545
    }
546
    Geometry[] array = new Geometry[geometries.size()];
547
    return geometryFactory.createGeometryCollection((Geometry[]) geometries.toArray(array));
548
  } */
549
}
550