1 /**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2005-2006 Refractions Research Inc.
7 * Copyright (C) 2001-2002 Vivid Solutions Inc.
8 *
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.
13 *
14 **********************************************************************
15 *
16 * Last port: io/WKTReader.java rev. 1.1 (JTS-1.7)
17 *
18 **********************************************************************/
19
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/CoordinateArraySequence.h>
36 #include <geos/geom/PrecisionModel.h>
37 #include <geos/inline.h>
38 #include <geos/util.h>
39
40 #include <sstream>
41 #include <string>
42 #include <cassert>
43
44 #ifndef GEOS_DEBUG
45 #define GEOS_DEBUG 0
46 #endif
47
48 #ifdef GEOS_DEBUG
49 #include <iostream>
50 #endif
51
52 #ifndef GEOS_INLINE
53 #include <geos/io/WKTReader.inl>
54 #endif
55
56 using namespace std;
57 using namespace geos::geom;
58
59 namespace geos {
60 namespace io { // geos.io
61
62 std::unique_ptr<Geometry>
read(const string & wellKnownText)63 WKTReader::read(const string& wellKnownText)
64 {
65 CLocalizer clocale;
66 StringTokenizer tokenizer(wellKnownText);
67 return readGeometryTaggedText(&tokenizer);
68 }
69
70 std::unique_ptr<CoordinateSequence>
getCoordinates(StringTokenizer * tokenizer)71 WKTReader::getCoordinates(StringTokenizer* tokenizer)
72 {
73 size_t dim = 2;
74 string nextToken = getNextEmptyOrOpener(tokenizer, dim);
75 if(nextToken == "EMPTY") {
76 return geometryFactory->getCoordinateSequenceFactory()->create(std::size_t(0), dim);
77 }
78
79 Coordinate coord;
80 getPreciseCoordinate(tokenizer, coord, dim);
81 auto coordinates = detail::make_unique<CoordinateArraySequence>(0, dim);
82 coordinates->add(coord);
83
84 nextToken = getNextCloserOrComma(tokenizer);
85 while(nextToken == ",") {
86 getPreciseCoordinate(tokenizer, coord, dim);
87 coordinates->add(coord);
88 nextToken = getNextCloserOrComma(tokenizer);
89 }
90
91 return std::move(coordinates);
92 }
93
94
95 void
getPreciseCoordinate(StringTokenizer * tokenizer,Coordinate & coord,size_t & dim)96 WKTReader::getPreciseCoordinate(StringTokenizer* tokenizer,
97 Coordinate& coord,
98 size_t& dim)
99 {
100 coord.x = getNextNumber(tokenizer);
101 coord.y = getNextNumber(tokenizer);
102 if(isNumberNext(tokenizer)) {
103 coord.z = getNextNumber(tokenizer);
104 dim = 3;
105
106 // If there is a fourth value (M) read and discard it.
107 if(isNumberNext(tokenizer)) {
108 getNextNumber(tokenizer);
109 }
110
111 }
112 else {
113 coord.z = DoubleNotANumber;
114 dim = 2;
115 }
116 precisionModel->makePrecise(coord);
117 }
118
119 bool
isNumberNext(StringTokenizer * tokenizer)120 WKTReader::isNumberNext(StringTokenizer* tokenizer)
121 {
122 return tokenizer->peekNextToken() == StringTokenizer::TT_NUMBER;
123 }
124
125 double
getNextNumber(StringTokenizer * tokenizer)126 WKTReader::getNextNumber(StringTokenizer* tokenizer)
127 {
128 int type = tokenizer->nextToken();
129 switch(type) {
130 case StringTokenizer::TT_EOF:
131 throw ParseException("Expected number but encountered end of stream");
132 case StringTokenizer::TT_EOL:
133 throw ParseException("Expected number but encountered end of line");
134 case StringTokenizer::TT_NUMBER:
135 return tokenizer->getNVal();
136 case StringTokenizer::TT_WORD:
137 throw ParseException("Expected number but encountered word", tokenizer->getSVal());
138 case '(':
139 throw ParseException("Expected number but encountered '('");
140 case ')':
141 throw ParseException("Expected number but encountered ')'");
142 case ',':
143 throw ParseException("Expected number but encountered ','");
144 }
145 assert(0); // Encountered unexpected StreamTokenizer type
146 return 0;
147 }
148
149 string
getNextEmptyOrOpener(StringTokenizer * tokenizer,std::size_t & dim)150 WKTReader::getNextEmptyOrOpener(StringTokenizer* tokenizer, std::size_t& dim)
151 {
152 string nextWord = getNextWord(tokenizer);
153
154 // Skip the Z, M or ZM of an SF1.2 3/4 dim coordinate.
155 if(nextWord == "Z" || nextWord == "ZM") {
156 dim = 3;
157 }
158
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);
162 }
163
164 if(nextWord == "EMPTY" || nextWord == "(") {
165 return nextWord;
166 }
167 throw ParseException("Expected 'Z', 'M', 'ZM', 'EMPTY' or '(' but encountered ", nextWord);
168 }
169
170 string
getNextCloserOrComma(StringTokenizer * tokenizer)171 WKTReader::getNextCloserOrComma(StringTokenizer* tokenizer)
172 {
173 string nextWord = getNextWord(tokenizer);
174 if(nextWord == "," || nextWord == ")") {
175 return nextWord;
176 }
177 throw ParseException("Expected ')' or ',' but encountered", nextWord);
178 }
179
180 string
getNextCloser(StringTokenizer * tokenizer)181 WKTReader::getNextCloser(StringTokenizer* tokenizer)
182 {
183 string nextWord = getNextWord(tokenizer);
184 if(nextWord == ")") {
185 return nextWord;
186 }
187 throw ParseException("Expected ')' but encountered", nextWord);
188 }
189
190 string
getNextWord(StringTokenizer * tokenizer)191 WKTReader::getNextWord(StringTokenizer* tokenizer)
192 {
193 int type = tokenizer->nextToken();
194 switch(type) {
195 case StringTokenizer::TT_EOF:
196 throw ParseException("Expected word but encountered end of stream");
197 case StringTokenizer::TT_EOL:
198 throw ParseException("Expected word but encountered end of line");
199 case StringTokenizer::TT_NUMBER:
200 throw ParseException("Expected word but encountered number", tokenizer->getNVal());
201 case StringTokenizer::TT_WORD: {
202 string word = tokenizer->getSVal();
203 int i = static_cast<int>(word.size());
204
205 while(--i >= 0) {
206 word[i] = static_cast<char>(toupper(word[i]));
207 }
208 return word;
209 }
210 case '(':
211 return "(";
212 case ')':
213 return ")";
214 case ',':
215 return ",";
216 }
217 assert(0);
218 //throw ParseException("Encountered unexpected StreamTokenizer type");
219 return "";
220 }
221
222 std::unique_ptr<Geometry>
readGeometryTaggedText(StringTokenizer * tokenizer)223 WKTReader::readGeometryTaggedText(StringTokenizer* tokenizer)
224 {
225 string type = getNextWord(tokenizer);
226 if(type == "POINT") {
227 return readPointText(tokenizer);
228 }
229 else if(type == "LINESTRING") {
230 return readLineStringText(tokenizer);
231 }
232 else if(type == "LINEARRING") {
233 return readLinearRingText(tokenizer);
234 }
235 else if(type == "POLYGON") {
236 return readPolygonText(tokenizer);
237 }
238 else if(type == "MULTIPOINT") {
239 return readMultiPointText(tokenizer);
240 }
241 else if(type == "MULTILINESTRING") {
242 return readMultiLineStringText(tokenizer);
243 }
244 else if(type == "MULTIPOLYGON") {
245 return readMultiPolygonText(tokenizer);
246 }
247 else if(type == "GEOMETRYCOLLECTION") {
248 return readGeometryCollectionText(tokenizer);
249 }
250 throw ParseException("Unknown type", type);
251 }
252
253 std::unique_ptr<Point>
readPointText(StringTokenizer * tokenizer)254 WKTReader::readPointText(StringTokenizer* tokenizer)
255 {
256 size_t dim = 2;
257 string nextToken = getNextEmptyOrOpener(tokenizer, dim);
258 if(nextToken == "EMPTY") {
259 return geometryFactory->createPoint(dim);
260 }
261
262 Coordinate coord;
263 getPreciseCoordinate(tokenizer, coord, dim);
264 getNextCloser(tokenizer);
265
266 return std::unique_ptr<Point>(geometryFactory->createPoint(coord));
267 }
268
269 std::unique_ptr<LineString>
readLineStringText(StringTokenizer * tokenizer)270 WKTReader::readLineStringText(StringTokenizer* tokenizer)
271 {
272 auto&& coords = getCoordinates(tokenizer);
273 return geometryFactory->createLineString(std::move(coords));
274 }
275
276 std::unique_ptr<LinearRing>
readLinearRingText(StringTokenizer * tokenizer)277 WKTReader::readLinearRingText(StringTokenizer* tokenizer)
278 {
279 auto&& coords = getCoordinates(tokenizer);
280 return geometryFactory->createLinearRing(std::move(coords));
281 }
282
283 std::unique_ptr<MultiPoint>
readMultiPointText(StringTokenizer * tokenizer)284 WKTReader::readMultiPointText(StringTokenizer* tokenizer)
285 {
286 size_t dim = 2;
287 string nextToken = getNextEmptyOrOpener(tokenizer, dim);
288 if(nextToken == "EMPTY") {
289 return geometryFactory->createMultiPoint();
290 }
291
292 int tok = tokenizer->peekNextToken();
293
294 if(tok == StringTokenizer::TT_NUMBER) {
295
296 // Try to parse deprecated form "MULTIPOINT(0 0, 1 1)"
297 auto coords = detail::make_unique<CoordinateArraySequence>();
298
299 do {
300 Coordinate coord;
301 getPreciseCoordinate(tokenizer, coord, dim);
302 coords->add(coord);
303 nextToken = getNextCloserOrComma(tokenizer);
304 }
305 while(nextToken == ",");
306
307 return std::unique_ptr<MultiPoint>(geometryFactory->createMultiPoint(*coords));
308 }
309
310 else if(tok == '(') {
311 // Try to parse correct form "MULTIPOINT((0 0), (1 1))"
312 std::vector<std::unique_ptr<Point>> points;
313
314 do {
315 points.push_back(readPointText(tokenizer));
316 nextToken = getNextCloserOrComma(tokenizer);
317 } while(nextToken == ",");
318
319 return geometryFactory->createMultiPoint(std::move(points));
320 }
321
322 else {
323 stringstream err;
324 err << "Unexpected token: ";
325 switch(tok) {
326 case StringTokenizer::TT_WORD:
327 err << "WORD " << tokenizer->getSVal();
328 break;
329 case StringTokenizer::TT_NUMBER:
330 err << "NUMBER " << tokenizer->getNVal();
331 break;
332 case StringTokenizer::TT_EOF:
333 case StringTokenizer::TT_EOL:
334 err << "EOF or EOL";
335 break;
336 case '(':
337 err << "(";
338 break;
339 case ')':
340 err << ")";
341 break;
342 case ',':
343 err << ",";
344 break;
345 default:
346 err << "??";
347 break;
348 }
349 err << endl;
350 throw ParseException(err.str());
351 }
352 }
353
354 std::unique_ptr<Polygon>
readPolygonText(StringTokenizer * tokenizer)355 WKTReader::readPolygonText(StringTokenizer* tokenizer)
356 {
357 size_t dim = 2;
358 string nextToken = getNextEmptyOrOpener(tokenizer, dim);
359 if(nextToken == "EMPTY") {
360 return geometryFactory->createPolygon(dim);
361 }
362
363 std::vector<std::unique_ptr<LinearRing>> holes;
364 auto shell = readLinearRingText(tokenizer);
365 nextToken = getNextCloserOrComma(tokenizer);
366 while(nextToken == ",") {
367 holes.push_back(readLinearRingText(tokenizer));
368 nextToken = getNextCloserOrComma(tokenizer);
369 }
370
371 return geometryFactory->createPolygon(std::move(shell), std::move(holes));
372 }
373
374 std::unique_ptr<MultiLineString>
readMultiLineStringText(StringTokenizer * tokenizer)375 WKTReader::readMultiLineStringText(StringTokenizer* tokenizer)
376 {
377 size_t dim = 2;
378 string nextToken = getNextEmptyOrOpener(tokenizer, dim);
379 if(nextToken == "EMPTY") {
380 return geometryFactory->createMultiLineString();
381 }
382
383 std::vector<std::unique_ptr<LineString>> lineStrings;
384 do {
385 lineStrings.push_back(readLineStringText(tokenizer));
386 nextToken = getNextCloserOrComma(tokenizer);
387 } while (nextToken == ",");
388
389 return geometryFactory->createMultiLineString(std::move(lineStrings));
390 }
391
392 std::unique_ptr<MultiPolygon>
readMultiPolygonText(StringTokenizer * tokenizer)393 WKTReader::readMultiPolygonText(StringTokenizer* tokenizer)
394 {
395 size_t dim = 2;
396 string nextToken = getNextEmptyOrOpener(tokenizer, dim);
397 if(nextToken == "EMPTY") {
398 return geometryFactory->createMultiPolygon();
399 }
400
401 std::vector<std::unique_ptr<Polygon>> polygons;
402 do {
403 polygons.push_back(readPolygonText(tokenizer));
404 nextToken = getNextCloserOrComma(tokenizer);
405 } while(nextToken == ",");
406
407 return geometryFactory->createMultiPolygon(std::move(polygons));
408 }
409
410 std::unique_ptr<GeometryCollection>
readGeometryCollectionText(StringTokenizer * tokenizer)411 WKTReader::readGeometryCollectionText(StringTokenizer* tokenizer)
412 {
413 size_t dim = 2;
414 string nextToken = getNextEmptyOrOpener(tokenizer, dim);
415 if(nextToken == "EMPTY") {
416 return geometryFactory->createGeometryCollection();
417 }
418
419 std::vector<std::unique_ptr<Geometry>> geoms;
420 do {
421 geoms.push_back(readGeometryTaggedText(tokenizer));
422 nextToken = getNextCloserOrComma(tokenizer);
423 } while(nextToken == ",");
424
425 return geometryFactory->createGeometryCollection(std::move(geoms));
426 }
427
428 } // namespace geos.io
429 } // namespace geos
430