Complete Note#1 in the http://wiki.osgeo.org/wiki/GEOS_Provenance_Review to get out...
[geos.git] / src / io / WKTReader.cpp
blob769108e8bcfff91546c0c723c3a040e7e3ef5617
1 /**********************************************************************
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
6 * Copyright (C) 2005-2006 Refractions Research Inc.
7 * Copyright (C) 2001-2002 Vivid Solutions Inc.
9 * This is free software; you can redistribute and/or modify it under
10 * the terms of the GNU Lesser General Public Licence as published
11 * by the Free Software Foundation.
12 * See the COPYING file for more information.
14 **********************************************************************
16 * Last port: io/WKTReader.java rev. 1.1 (JTS-1.7)
18 **********************************************************************/
20 #include <geos/io/WKTReader.h>
21 #include <geos/io/StringTokenizer.h>
22 #include <geos/io/ParseException.h>
23 #include <geos/io/CLocalizer.h>
24 #include <geos/geom/GeometryFactory.h>
25 #include <geos/geom/Coordinate.h>
26 #include <geos/geom/Point.h>
27 #include <geos/geom/LinearRing.h>
28 #include <geos/geom/LineString.h>
29 #include <geos/geom/Polygon.h>
30 #include <geos/geom/MultiPoint.h>
31 #include <geos/geom/MultiLineString.h>
32 #include <geos/geom/MultiPolygon.h>
33 #include <geos/geom/CoordinateSequenceFactory.h>
34 #include <geos/geom/CoordinateSequence.h>
35 #include <geos/geom/PrecisionModel.h>
36 #include <geos/inline.h>
38 #include <sstream>
39 #include <string>
40 #include <cassert>
42 #ifndef GEOS_DEBUG
43 #define GEOS_DEBUG 0
44 #endif
46 #ifdef GEOS_DEBUG
47 #include <iostream>
48 #endif
50 #ifndef GEOS_INLINE
51 #include <geos/io/WKTReader.inl>
52 #endif
54 using namespace std;
55 using namespace geos::geom;
57 namespace geos {
58 namespace io { // geos.io
60 Geometry *
61 WKTReader::read(const string &wellKnownText)
63 //auto_ptr<StringTokenizer> tokenizer(new StringTokenizer(wellKnownText));
64 CLocalizer clocale;
65 StringTokenizer tokenizer(wellKnownText);
66 Geometry *g=NULL;
67 g=readGeometryTaggedText(&tokenizer);
68 return g;
71 CoordinateSequence*
72 WKTReader::getCoordinates(StringTokenizer *tokenizer)
74 size_t dim;
75 string nextToken=getNextEmptyOrOpener(tokenizer);
76 if (nextToken=="EMPTY") {
77 return geometryFactory->getCoordinateSequenceFactory()->create(NULL);
78 //new CoordinateArraySequence();
81 Coordinate coord;
82 getPreciseCoordinate(tokenizer, coord, dim);
84 CoordinateSequence *coordinates = \
85 geometryFactory->getCoordinateSequenceFactory()->create((size_t)0,dim);
86 coordinates->add(coord);
87 try {
88 nextToken=getNextCloserOrComma(tokenizer);
89 while (nextToken==",") {
90 getPreciseCoordinate(tokenizer, coord, dim );
91 coordinates->add(coord);
92 nextToken=getNextCloserOrComma(tokenizer);
94 } catch (...) {
95 delete coordinates;
96 throw;
99 return coordinates;
102 void
103 WKTReader::getPreciseCoordinate(StringTokenizer *tokenizer,
104 Coordinate& coord,
105 size_t &dim )
107 coord.x=getNextNumber(tokenizer);
108 coord.y=getNextNumber(tokenizer);
109 if (isNumberNext(tokenizer)) {
110 coord.z=getNextNumber(tokenizer);
111 dim = 3;
113 // If there is a fourth value (M) read and discard it.
114 if (isNumberNext(tokenizer))
115 getNextNumber(tokenizer);
117 } else {
118 coord.z=DoubleNotANumber;
119 dim = 2;
121 precisionModel->makePrecise(coord);
124 bool
125 WKTReader::isNumberNext(StringTokenizer *tokenizer)
127 return tokenizer->peekNextToken()==StringTokenizer::TT_NUMBER;
130 double
131 WKTReader::getNextNumber(StringTokenizer *tokenizer)
133 int type=tokenizer->nextToken();
134 switch(type){
135 case StringTokenizer::TT_EOF:
136 throw ParseException("Expected number but encountered end of stream");
137 case StringTokenizer::TT_EOL:
138 throw ParseException("Expected number but encountered end of line");
139 case StringTokenizer::TT_NUMBER:
140 return tokenizer->getNVal();
141 case StringTokenizer::TT_WORD:
142 throw ParseException("Expected number but encountered word",tokenizer->getSVal());
143 case '(':
144 throw ParseException("Expected number but encountered '('");
145 case ')':
146 throw ParseException("Expected number but encountered ')'");
147 case ',':
148 throw ParseException("Expected number but encountered ','");
150 assert(0); // Encountered unexpected StreamTokenizer type
151 return 0;
154 string
155 WKTReader::getNextEmptyOrOpener(StringTokenizer *tokenizer)
157 string nextWord=getNextWord(tokenizer);
159 // Skip the Z, M or ZM of an SF1.2 3/4 dim coordinate.
160 if (nextWord == "Z" || nextWord == "M" || nextWord == "ZM" )
161 nextWord = getNextWord(tokenizer);
163 if (nextWord=="EMPTY" || nextWord=="(") {
164 return nextWord;
166 throw ParseException("Expected 'Z', 'M', 'ZM', 'EMPTY' or '(' but encountered ",nextWord);
169 string
170 WKTReader::getNextCloserOrComma(StringTokenizer *tokenizer)
172 string nextWord=getNextWord(tokenizer);
173 if (nextWord=="," || nextWord==")") {
174 return nextWord;
176 throw ParseException("Expected ')' or ',' but encountered",nextWord);
179 string
180 WKTReader::getNextCloser(StringTokenizer *tokenizer)
182 string nextWord=getNextWord(tokenizer);
183 if (nextWord==")") {
184 return nextWord;
186 throw ParseException("Expected ')' but encountered",nextWord);
189 string
190 WKTReader::getNextWord(StringTokenizer *tokenizer)
192 int type=tokenizer->nextToken();
193 switch(type){
194 case StringTokenizer::TT_EOF:
195 throw ParseException("Expected word but encountered end of stream");
196 case StringTokenizer::TT_EOL:
197 throw ParseException("Expected word but encountered end of line");
198 case StringTokenizer::TT_NUMBER:
199 throw ParseException("Expected word but encountered number", tokenizer->getNVal());
200 case StringTokenizer::TT_WORD:
202 string word = tokenizer->getSVal();
203 int i = word.size();
205 while( --i >= 0 )
207 word[i] = static_cast<char>(toupper(word[i]));
209 return word;
211 case '(':
212 return "(";
213 case ')':
214 return ")";
215 case ',':
216 return ",";
218 assert(0);
219 //throw ParseException("Encountered unexpected StreamTokenizer type");
220 return "";
223 Geometry*
224 WKTReader::readGeometryTaggedText(StringTokenizer *tokenizer)
226 string type = getNextWord(tokenizer);
227 if (type=="POINT") {
228 return readPointText(tokenizer);
229 } else if (type=="LINESTRING") {
230 return readLineStringText(tokenizer);
231 } else if (type=="LINEARRING") {
232 return readLinearRingText(tokenizer);
233 } else if (type=="POLYGON") {
234 return readPolygonText(tokenizer);
235 } else if (type=="MULTIPOINT") {
236 return readMultiPointText(tokenizer);
237 } else if (type=="MULTILINESTRING") {
238 return readMultiLineStringText(tokenizer);
239 } else if (type=="MULTIPOLYGON") {
240 return readMultiPolygonText(tokenizer);
241 } else if (type=="GEOMETRYCOLLECTION") {
242 return readGeometryCollectionText(tokenizer);
244 throw ParseException("Unknown type",type);
247 Point*
248 WKTReader::readPointText(StringTokenizer *tokenizer)
250 size_t dim;
251 string nextToken=getNextEmptyOrOpener(tokenizer);
252 if (nextToken=="EMPTY") {
253 return geometryFactory->createPoint(Coordinate::getNull());
256 Coordinate coord;
257 getPreciseCoordinate(tokenizer, coord, dim);
258 getNextCloser(tokenizer);
260 return geometryFactory->createPoint(coord);
263 LineString* WKTReader::readLineStringText(StringTokenizer *tokenizer) {
264 CoordinateSequence *coords = getCoordinates(tokenizer);
265 LineString *ret = geometryFactory->createLineString(coords);
266 return ret;
269 LinearRing* WKTReader::readLinearRingText(StringTokenizer *tokenizer) {
270 CoordinateSequence *coords = getCoordinates(tokenizer);
271 LinearRing *ret;
272 ret = geometryFactory->createLinearRing(coords);
273 return ret;
276 MultiPoint*
277 WKTReader::readMultiPointText(StringTokenizer *tokenizer)
279 string nextToken=getNextEmptyOrOpener(tokenizer);
280 if (nextToken=="EMPTY") {
281 return geometryFactory->createMultiPoint();
284 int tok = tokenizer->peekNextToken();
286 if ( tok == StringTokenizer::TT_NUMBER )
288 size_t dim;
290 // Try to parse deprecated form "MULTIPOINT(0 0, 1 1)"
291 const CoordinateSequenceFactory* csf = \
292 geometryFactory->getCoordinateSequenceFactory();
293 CoordinateSequence *coords = csf->create(NULL);
294 try {
295 do {
296 Coordinate coord;
297 getPreciseCoordinate(tokenizer, coord, dim);
298 coords->add(coord);
299 nextToken=getNextCloserOrComma(tokenizer);
300 } while(nextToken == ",");
302 MultiPoint *ret = geometryFactory->createMultiPoint(*coords);
303 delete coords;
304 return ret;
305 } catch (...) {
306 delete coords;
307 throw;
311 else if ( tok == '(' )
313 // Try to parse correct form "MULTIPOINT((0 0), (1 1))"
314 vector<Geometry *> *points=new vector<Geometry *>();
315 try {
316 do {
317 Point *point=readPointText(tokenizer);
318 points->push_back(point);
319 nextToken=getNextCloserOrComma(tokenizer);
320 } while(nextToken == ",");
321 return geometryFactory->createMultiPoint(points);
322 } catch (...) {
323 // clean up
324 for (size_t i=0; i<points->size(); i++)
326 delete (*points)[i];
328 delete points;
329 throw;
333 else
335 stringstream err;
336 err << "Unexpected token: ";
337 switch (tok)
339 case StringTokenizer::TT_WORD:
340 err << "WORD " << tokenizer->getSVal();
341 break;
342 case StringTokenizer::TT_NUMBER:
343 err << "NUMBER " << tokenizer->getNVal();
344 break;
345 case StringTokenizer::TT_EOF:
346 case StringTokenizer::TT_EOL:
347 err << "EOF or EOL";
348 break;
349 case '(':
350 err << "(";
351 break;
352 case ')':
353 err << ")";
354 break;
355 case ',':
356 err << ",";
357 break;
358 default:
359 err << "??";
360 break;
362 err << endl;
363 throw ParseException(err.str());
367 Polygon*
368 WKTReader::readPolygonText(StringTokenizer *tokenizer)
370 Polygon *poly=NULL;
371 LinearRing *shell=NULL;
372 string nextToken=getNextEmptyOrOpener(tokenizer);
373 if (nextToken=="EMPTY") {
374 return geometryFactory->createPolygon(NULL,NULL);
377 vector<Geometry *> *holes=new vector<Geometry *>();
378 try {
379 shell=readLinearRingText(tokenizer);
380 nextToken=getNextCloserOrComma(tokenizer);
381 while(nextToken==",") {
382 LinearRing *hole=readLinearRingText(tokenizer);
383 holes->push_back(hole);
384 nextToken=getNextCloserOrComma(tokenizer);
386 poly = geometryFactory->createPolygon(shell,holes);
387 } catch (...) {
388 for (unsigned int i=0; i<holes->size(); i++)
389 delete (*holes)[i];
390 delete holes;
391 delete shell;
392 throw;
394 return poly;
397 MultiLineString* WKTReader::readMultiLineStringText(StringTokenizer *tokenizer) {
398 string nextToken=getNextEmptyOrOpener(tokenizer);
399 if (nextToken=="EMPTY") {
400 return geometryFactory->createMultiLineString(NULL);
402 vector<Geometry *> *lineStrings=new vector<Geometry *>();
403 LineString *lineString=readLineStringText(tokenizer);
404 lineStrings->push_back(lineString);
405 nextToken=getNextCloserOrComma(tokenizer);
406 while(nextToken==",") {
407 LineString *lineString=readLineStringText(tokenizer);
408 lineStrings->push_back(lineString);
409 nextToken=getNextCloserOrComma(tokenizer);
411 MultiLineString *ret = geometryFactory->createMultiLineString(lineStrings);
412 //for (int i=0; i<lineStrings->size(); i++) delete (*lineStrings)[i];
413 //delete lineStrings;
414 return ret;
417 MultiPolygon* WKTReader::readMultiPolygonText(StringTokenizer *tokenizer) {
418 string nextToken=getNextEmptyOrOpener(tokenizer);
419 if (nextToken=="EMPTY") {
420 return geometryFactory->createMultiPolygon(NULL);
422 vector<Geometry *> *polygons=new vector<Geometry *>();
423 Polygon *polygon=readPolygonText(tokenizer);
424 polygons->push_back(polygon);
425 nextToken=getNextCloserOrComma(tokenizer);
426 while(nextToken==",") {
427 Polygon *polygon=readPolygonText(tokenizer);
428 polygons->push_back(polygon);
429 nextToken=getNextCloserOrComma(tokenizer);
431 MultiPolygon *ret = geometryFactory->createMultiPolygon(polygons);
432 //for (int i=0; i<polygons->size(); i++) delete (*polygons)[i];
433 //delete polygons;
434 return ret;
437 GeometryCollection* WKTReader::readGeometryCollectionText(StringTokenizer *tokenizer) {
438 string nextToken=getNextEmptyOrOpener(tokenizer);
439 if (nextToken=="EMPTY") {
440 return geometryFactory->createGeometryCollection(NULL);
442 vector<Geometry *> *geoms=new vector<Geometry *>();
443 Geometry *geom;
444 geom=readGeometryTaggedText(tokenizer);
445 geoms->push_back(geom);
446 nextToken=getNextCloserOrComma(tokenizer);
447 while(nextToken==",") {
448 geom=readGeometryTaggedText(tokenizer);
449 geoms->push_back(geom);
450 nextToken=getNextCloserOrComma(tokenizer);
452 GeometryCollection *ret = geometryFactory->createGeometryCollection(geoms);
453 //for (int i=0; i<geoms->size(); i++) delete (*geoms)[i];
454 //delete geoms;
455 return ret;
458 } // namespace geos.io
459 } // namespace geos
461 /**********************************************************************
462 * $Log$
463 * Revision 1.42 2006/04/26 16:35:40 strk
464 * Had WKTReader accept correct form for MultiPoint
466 * Revision 1.41 2006/04/10 12:05:35 strk
467 * Added inline-replicator implementation files to make sure
468 * functions in .inl files are still available out-of-line.
469 * A side effect is this should fix MingW build.
471 * Revision 1.40 2006/03/24 09:52:41 strk
472 * USE_INLINE => GEOS_INLINE
474 * Revision 1.39 2006/03/22 16:58:35 strk
475 * Removed (almost) all inclusions of geom.h.
476 * Removed obsoleted .cpp files.
477 * Fixed a bug in WKTReader not using the provided CoordinateSequence
478 * implementation, optimized out some memory allocations.
480 **********************************************************************/