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