/src/geos/src/io/WKTReader.cpp
Line | Count | Source |
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/Coordinate.h> |
25 | | #include <geos/geom/CircularString.h> |
26 | | #include <geos/geom/CompoundCurve.h> |
27 | | #include <geos/geom/Point.h> |
28 | | #include <geos/geom/LinearRing.h> |
29 | | #include <geos/geom/LineString.h> |
30 | | #include <geos/geom/Polygon.h> |
31 | | #include <geos/geom/CurvePolygon.h> |
32 | | #include <geos/geom/MultiPoint.h> |
33 | | #include <geos/geom/MultiLineString.h> |
34 | | #include <geos/geom/MultiCurve.h> |
35 | | #include <geos/geom/MultiPolygon.h> |
36 | | #include <geos/geom/MultiSurface.h> |
37 | | #include <geos/geom/CoordinateSequence.h> |
38 | | #include <geos/geom/Surface.h> |
39 | | #include <geos/util.h> |
40 | | #include <geos/util/string.h> |
41 | | |
42 | | #include <sstream> |
43 | | #include <string> |
44 | | #include <cassert> |
45 | | |
46 | | |
47 | | using namespace geos::geom; |
48 | | |
49 | | namespace geos { |
50 | | namespace io { // geos.io |
51 | | |
52 | | static constexpr int MAX_PARSE_DEPTH = 100; |
53 | | |
54 | | std::unique_ptr<Geometry> |
55 | | WKTReader::read(const std::string& wellKnownText) const |
56 | 9.03k | { |
57 | 9.03k | parseDepth_ = 0; |
58 | 9.03k | CLocalizer clocale; |
59 | 9.03k | StringTokenizer tokenizer(wellKnownText); |
60 | 9.03k | OrdinateSet ordinateFlags = OrdinateSet::createXY(); |
61 | 9.03k | auto ret = readGeometryTaggedText(&tokenizer, ordinateFlags); |
62 | | |
63 | 9.03k | if (tokenizer.peekNextToken() != StringTokenizer::TT_EOF) { |
64 | 59 | tokenizer.nextToken(); |
65 | 59 | throw ParseException("Unexpected text after end of geometry"); |
66 | 59 | } |
67 | | |
68 | 8.97k | return ret; |
69 | 9.03k | } |
70 | | |
71 | | |
72 | | std::unique_ptr<CoordinateSequence> |
73 | | WKTReader::readCoordinates(const std::string& wellKnownText) const |
74 | 0 | { |
75 | 0 | CLocalizer clocale; |
76 | 0 | StringTokenizer tokenizer(wellKnownText); |
77 | 0 | OrdinateSet ordinateFlags = OrdinateSet::createXY(); |
78 | 0 | auto ret = getCoordinates(&tokenizer, ordinateFlags); |
79 | |
|
80 | 0 | if (tokenizer.peekNextToken() != StringTokenizer::TT_EOF) { |
81 | 0 | tokenizer.nextToken(); |
82 | 0 | throw ParseException("Unexpected text after end of geometry"); |
83 | 0 | } |
84 | | |
85 | 0 | return ret; |
86 | 0 | } |
87 | | |
88 | | std::unique_ptr<CoordinateSequence> |
89 | | WKTReader::getCoordinates(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
90 | 2.23M | { |
91 | 2.23M | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
92 | 2.23M | if(nextToken == "EMPTY") { |
93 | 4.07k | return detail::make_unique<CoordinateSequence>(0u, ordinateFlags.hasZ(), ordinateFlags.hasM()); |
94 | 4.07k | } |
95 | | |
96 | 2.23M | CoordinateXYZM coord(0, 0, DoubleNotANumber, DoubleNotANumber); |
97 | 2.23M | getPreciseCoordinate(tokenizer, ordinateFlags, coord); |
98 | | |
99 | 2.23M | auto coordinates = detail::make_unique<CoordinateSequence>(0u, ordinateFlags.hasZ(), ordinateFlags.hasM()); |
100 | 2.23M | coordinates->add(coord); |
101 | | |
102 | 2.23M | nextToken = getNextCloserOrComma(tokenizer); |
103 | 13.3M | while(nextToken == ",") { |
104 | 11.1M | getPreciseCoordinate(tokenizer, ordinateFlags, coord); |
105 | 11.1M | coordinates->add(coord); |
106 | 11.1M | nextToken = getNextCloserOrComma(tokenizer); |
107 | 11.1M | } |
108 | | |
109 | 2.23M | return coordinates; |
110 | 2.23M | } |
111 | | |
112 | | void |
113 | | WKTReader::getPreciseCoordinate(StringTokenizer* tokenizer, |
114 | | OrdinateSet& ordinateFlags, |
115 | 17.3M | CoordinateXYZM& coord) const { |
116 | 17.3M | coord.x = getNextNumber(tokenizer); |
117 | 17.3M | coord.y = getNextNumber(tokenizer); |
118 | | |
119 | | // Check for undeclared Z dimension |
120 | 17.3M | if (ordinateFlags.changesAllowed() && isNumberNext(tokenizer)) { |
121 | 3.54k | ordinateFlags.setZ(true); |
122 | 3.54k | } |
123 | | |
124 | 17.3M | if (ordinateFlags.hasZ()) { |
125 | 9.89k | coord.z = getNextNumber(tokenizer); |
126 | 9.89k | } |
127 | | |
128 | | // Check for undeclared M dimension |
129 | 17.3M | if (ordinateFlags.changesAllowed() && ordinateFlags.hasZ() && isNumberNext(tokenizer)) { |
130 | 1.40k | ordinateFlags.setM(true); |
131 | 1.40k | } |
132 | | |
133 | 17.3M | if (ordinateFlags.hasM()) { |
134 | 35.4k | coord.m = getNextNumber(tokenizer); |
135 | 35.4k | } |
136 | | |
137 | 17.3M | ordinateFlags.setChangesAllowed(false); // First coordinate read; future coordinates must be consistent |
138 | | |
139 | 17.3M | precisionModel->makePrecise(coord); |
140 | 17.3M | } |
141 | | |
142 | | bool |
143 | | WKTReader::isNumberNext(StringTokenizer* tokenizer) |
144 | 33.9k | { |
145 | 33.9k | return tokenizer->peekNextToken() == StringTokenizer::TT_NUMBER; |
146 | 33.9k | } |
147 | | |
148 | | bool |
149 | | WKTReader::isOpenerNext(StringTokenizer* tokenizer) |
150 | 0 | { |
151 | 0 | return tokenizer->peekNextToken() == '('; |
152 | 0 | } |
153 | | |
154 | | double |
155 | | WKTReader::getNextNumber(StringTokenizer* tokenizer) |
156 | 34.6M | { |
157 | 34.6M | int type = tokenizer->nextToken(); |
158 | 34.6M | switch(type) { |
159 | 22 | case StringTokenizer::TT_EOF: |
160 | 22 | throw ParseException("Expected number but encountered end of stream"); |
161 | 0 | case StringTokenizer::TT_EOL: |
162 | 0 | throw ParseException("Expected number but encountered end of line"); |
163 | 34.6M | case StringTokenizer::TT_NUMBER: |
164 | 34.6M | return tokenizer->getNVal(); |
165 | 41 | case StringTokenizer::TT_WORD: |
166 | 41 | throw ParseException("Expected number but encountered word", tokenizer->getSVal()); |
167 | 4 | case '(': |
168 | 4 | throw ParseException("Expected number but encountered '('"); |
169 | 5 | case ')': |
170 | 5 | throw ParseException("Expected number but encountered ')'"); |
171 | 13 | case ',': |
172 | 13 | throw ParseException("Expected number but encountered ','"); |
173 | 34.6M | } |
174 | 34.6M | assert(0); // Encountered unexpected StreamTokenizer type |
175 | 0 | return 0; |
176 | 34.6M | } |
177 | | |
178 | | bool |
179 | 308k | WKTReader::isTypeName(const std::string & type, const std::string & typeName) { |
180 | 308k | auto l = type.size(); |
181 | 308k | auto r = typeName.size(); |
182 | 308k | if (l == r && type == typeName) return true; |
183 | 272k | if (l == r + 1 && (type == typeName + 'Z' || type == typeName + 'M')) return true; |
184 | 270k | if (l == r + 2 && type == typeName + "ZM") return true; |
185 | | |
186 | | // take `POINT` as example: |
187 | | // POI, POINTa, POINTZMx are all invalid |
188 | 269k | return false; |
189 | 270k | } |
190 | | |
191 | | void |
192 | 38.7k | WKTReader::readOrdinateFlags(const std::string & s, OrdinateSet& ordinateFlags) { |
193 | 38.7k | if (util::endsWith(s, "ZM")) { |
194 | 564 | ordinateFlags.setM(true); |
195 | 564 | ordinateFlags.setZ(true); |
196 | 564 | ordinateFlags.setChangesAllowed(false); |
197 | 38.1k | } else if (util::endsWith(s, 'M')) { |
198 | 2.30k | ordinateFlags.setM(true); |
199 | 2.30k | ordinateFlags.setChangesAllowed(false); |
200 | 35.8k | } else if (util::endsWith(s, 'Z')) { |
201 | 275 | ordinateFlags.setZ(true); |
202 | 275 | ordinateFlags.setChangesAllowed(false); |
203 | 275 | } |
204 | 38.7k | } |
205 | | |
206 | | std::string |
207 | | WKTReader::getNextEmptyOrOpener(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) |
208 | 3.96M | { |
209 | 3.96M | const bool changesAllowed = ordinateFlags.changesAllowed(); |
210 | 3.96M | std::string nextWord = getNextWord(tokenizer); |
211 | | |
212 | 3.96M | bool flagsModified = false; |
213 | | |
214 | | // Skip the Z, M or ZM of an SF1.2 3/4 dim coordinate. |
215 | 3.96M | if (nextWord == "ZM") { |
216 | 172 | if (!changesAllowed) { |
217 | 2 | throw ParseException("'ZM' found but 'ZM' or 'Z' or 'M' has been specified in previous type"); |
218 | 2 | } |
219 | 170 | ordinateFlags.setZ(true); |
220 | 170 | ordinateFlags.setM(true); |
221 | 170 | flagsModified = true; |
222 | 170 | nextWord = getNextWord(tokenizer); |
223 | 3.96M | } else { |
224 | 3.96M | if (nextWord == "Z") { |
225 | 636 | if (!changesAllowed) { |
226 | 1 | throw ParseException("'Z' found but 'ZM' or 'Z' or 'M' has been specified in previous type"); |
227 | 1 | } |
228 | 635 | ordinateFlags.setZ(true); |
229 | 635 | flagsModified = true; |
230 | 635 | nextWord = getNextWord(tokenizer); |
231 | 635 | } |
232 | | |
233 | 3.96M | if (nextWord == "M") { |
234 | 91 | if (!changesAllowed || flagsModified) { |
235 | 2 | throw ParseException("'M' found but 'ZM' or 'Z' or 'M' has been specified previously"); |
236 | 2 | } |
237 | 89 | ordinateFlags.setM(true); |
238 | 89 | flagsModified = true; |
239 | 89 | nextWord = getNextWord(tokenizer); |
240 | 89 | } |
241 | 3.96M | } |
242 | | |
243 | 3.96M | if (flagsModified) { |
244 | 888 | ordinateFlags.setChangesAllowed(false); |
245 | 888 | } |
246 | | |
247 | 3.96M | if(nextWord == "EMPTY" || nextWord == "(") { |
248 | 3.96M | return nextWord; |
249 | 3.96M | } |
250 | 150 | throw ParseException("Expected 'Z', 'M', 'ZM', 'EMPTY' or '(' but encountered ", nextWord); |
251 | 3.96M | } |
252 | | |
253 | | std::string |
254 | | WKTReader::getNextCloserOrComma(StringTokenizer* tokenizer) |
255 | 21.2M | { |
256 | 21.2M | std::string nextWord = getNextWord(tokenizer); |
257 | 21.2M | if(nextWord == "," || nextWord == ")") { |
258 | 21.2M | return nextWord; |
259 | 21.2M | } |
260 | 117 | throw ParseException("Expected ')' or ',' but encountered", nextWord); |
261 | 21.2M | } |
262 | | |
263 | | std::string |
264 | | WKTReader::getNextCloser(StringTokenizer* tokenizer) |
265 | 0 | { |
266 | 0 | std::string nextWord = getNextWord(tokenizer); |
267 | 0 | if(nextWord == ")") { |
268 | 0 | return nextWord; |
269 | 0 | } |
270 | 0 | throw ParseException("Expected ')' but encountered", nextWord); |
271 | 0 | } |
272 | | |
273 | | std::string |
274 | | WKTReader::getNextWord(StringTokenizer* tokenizer) |
275 | 25.2M | { |
276 | 25.2M | int type = tokenizer->nextToken(); |
277 | 25.2M | switch(type) { |
278 | 66 | case StringTokenizer::TT_EOF: |
279 | 66 | throw ParseException("Expected word but encountered end of stream"); |
280 | 0 | case StringTokenizer::TT_EOL: |
281 | 0 | throw ParseException("Expected word but encountered end of line"); |
282 | 25 | case StringTokenizer::TT_NUMBER: |
283 | 25 | throw ParseException("Expected word but encountered number", tokenizer->getNVal()); |
284 | 49.4k | case StringTokenizer::TT_WORD: { |
285 | 49.4k | std::string word = tokenizer->getSVal(); |
286 | 21.1M | for (char& c : word) { |
287 | | // Avoid UB if c is not representable as unsigned char |
288 | | // https://en.cppreference.com/w/cpp/string/byte/toupper |
289 | 21.1M | c = static_cast<char>(toupper(static_cast<unsigned char>(c))); |
290 | 21.1M | } |
291 | 49.4k | return word; |
292 | 0 | } |
293 | 3.96M | case '(': |
294 | 3.96M | return "("; |
295 | 3.95M | case ')': |
296 | 3.95M | return ")"; |
297 | 17.3M | case ',': |
298 | 17.3M | return ","; |
299 | 25.2M | } |
300 | 25.2M | assert(0); |
301 | | //throw ParseException("Encountered unexpected StreamTokenizer type"); |
302 | 0 | return ""; |
303 | 25.2M | } |
304 | | |
305 | | std::unique_ptr<Geometry> |
306 | | WKTReader::readGeometryTaggedText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags, const GeometryTypeId* emptyType) const |
307 | 41.8k | { |
308 | 41.8k | if (parseDepth_ >= MAX_PARSE_DEPTH) { |
309 | 7 | throw ParseException("Input geometry exceeds nesting depth limit"); |
310 | 7 | } |
311 | 41.8k | ++parseDepth_; |
312 | 41.8k | struct DepthGuard { int& d; ~DepthGuard() { --d; } } guard{parseDepth_}; |
313 | | |
314 | 41.8k | std::string type = getNextWord(tokenizer); |
315 | | |
316 | 41.8k | std::unique_ptr<Geometry> geom; |
317 | 41.8k | OrdinateSet origFlags = ordinateFlags; |
318 | | |
319 | 41.8k | OrdinateSet newFlags = OrdinateSet::createXY(); |
320 | 41.8k | if (type == "EMPTY") { |
321 | 3.07k | newFlags = origFlags; |
322 | 38.7k | } else { |
323 | 38.7k | readOrdinateFlags(type, newFlags); |
324 | 38.7k | } |
325 | | |
326 | 41.8k | if(isTypeName(type, "POINT")) { |
327 | 2.73k | geom = readPointText(tokenizer, newFlags); |
328 | 2.73k | } |
329 | 39.0k | else if(isTypeName(type, "LINESTRING")) { |
330 | 1.94k | geom = readLineStringText(tokenizer, newFlags); |
331 | 1.94k | } |
332 | 37.1k | else if(isTypeName(type, "LINEARRING")) { |
333 | 1.76k | geom = readLinearRingText(tokenizer, newFlags); |
334 | 1.76k | } |
335 | 35.3k | else if(isTypeName(type, "CIRCULARSTRING")) { |
336 | 3.65k | geom = readCircularStringText(tokenizer, newFlags); |
337 | 3.65k | } |
338 | 31.7k | else if(isTypeName(type, "COMPOUNDCURVE")) { |
339 | 1.16k | geom = readCompoundCurveText(tokenizer, newFlags); |
340 | 1.16k | } |
341 | 30.5k | else if(isTypeName(type, "POLYGON")) { |
342 | 2.77k | geom = readPolygonText(tokenizer, newFlags); |
343 | 2.77k | } |
344 | 27.8k | else if(isTypeName(type, "CURVEPOLYGON")) { |
345 | 611 | geom = readCurvePolygonText(tokenizer, newFlags); |
346 | 611 | } |
347 | 27.1k | else if(isTypeName(type, "MULTIPOINT")) { |
348 | 18.6k | geom = readMultiPointText(tokenizer, newFlags); |
349 | 18.6k | } |
350 | 8.52k | else if(isTypeName(type, "MULTILINESTRING")) { |
351 | 418 | geom = readMultiLineStringText(tokenizer, newFlags); |
352 | 418 | } |
353 | 8.11k | else if(isTypeName(type, "MULTICURVE")) { |
354 | 374 | geom = readMultiCurveText(tokenizer, newFlags); |
355 | 374 | } |
356 | 7.73k | else if(isTypeName(type, "MULTIPOLYGON")) { |
357 | 1.06k | geom = readMultiPolygonText(tokenizer, newFlags); |
358 | 1.06k | } |
359 | 6.67k | else if(isTypeName(type, "MULTISURFACE")) { |
360 | 237 | geom = readMultiSurfaceText(tokenizer, newFlags); |
361 | 237 | } |
362 | 6.43k | else if(isTypeName(type, "GEOMETRYCOLLECTION")) { |
363 | 2.98k | geom = readGeometryCollectionText(tokenizer, newFlags); |
364 | 3.44k | } else if (type == "EMPTY" && emptyType != nullptr) { |
365 | 3.07k | return geometryFactory->createEmptyGeometry(*emptyType, newFlags.hasZ(), newFlags.hasM()); |
366 | 3.07k | } else { |
367 | 374 | throw ParseException("Unknown type", type); |
368 | 374 | } |
369 | | |
370 | 38.3k | if (!origFlags.changesAllowed() && newFlags != origFlags) { |
371 | 12 | throw ParseException("Cannot mix dimensionality in a geometry."); |
372 | 12 | } |
373 | | |
374 | 38.3k | return geom; |
375 | | |
376 | 38.3k | } |
377 | | |
378 | | std::unique_ptr<Point> |
379 | | WKTReader::readPointText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
380 | 5.49k | { |
381 | 5.49k | auto&& coords = getCoordinates(tokenizer, ordinateFlags); |
382 | 5.49k | return geometryFactory->createPoint(std::move(coords)); |
383 | 5.49k | } |
384 | | |
385 | | std::unique_ptr<LineString> |
386 | | WKTReader::readLineStringText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
387 | 8.64k | { |
388 | 8.64k | auto&& coords = getCoordinates(tokenizer, ordinateFlags); |
389 | 8.64k | return geometryFactory->createLineString(std::move(coords)); |
390 | 8.64k | } |
391 | | |
392 | | std::unique_ptr<LinearRing> |
393 | | WKTReader::readLinearRingText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
394 | 2.21M | { |
395 | 2.21M | auto&& coords = getCoordinates(tokenizer, ordinateFlags); |
396 | 2.21M | if (fixStructure && !coords->isRing()) { |
397 | 0 | coords->closeRing(); |
398 | 0 | } |
399 | 2.21M | return geometryFactory->createLinearRing(std::move(coords)); |
400 | 2.21M | } |
401 | | |
402 | | std::unique_ptr<CircularString> |
403 | | WKTReader::readCircularStringText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
404 | 3.65k | { |
405 | 3.65k | auto&& coords = getCoordinates(tokenizer, ordinateFlags); |
406 | 3.65k | return geometryFactory->createCircularString(std::move(coords)); |
407 | 3.65k | } |
408 | | |
409 | | std::unique_ptr<Curve> |
410 | | WKTReader::readCurveText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
411 | 10.0k | { |
412 | 10.0k | int type = tokenizer->peekNextToken(); |
413 | 10.0k | if (type == '(') { |
414 | 4.15k | return readLineStringText(tokenizer, ordinateFlags); |
415 | 4.15k | } |
416 | | |
417 | 5.89k | GeometryTypeId defaultType = GEOS_LINESTRING; |
418 | 5.89k | auto component = readGeometryTaggedText(tokenizer, ordinateFlags, &defaultType); |
419 | 5.89k | if (dynamic_cast<Curve*>(component.get())) { |
420 | 4.95k | return std::unique_ptr<Curve>(static_cast<Curve*>(component.release())); |
421 | 4.95k | } |
422 | | |
423 | 942 | throw ParseException("Expected LINESTRING/CIRCULARSTRING/COMPOUNDCURVE but got " + component->getGeometryType()); |
424 | 5.89k | } |
425 | | |
426 | | std::unique_ptr<Geometry> |
427 | | WKTReader::readSurfaceText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
428 | 1.53k | { |
429 | 1.53k | int type = tokenizer->peekNextToken(); |
430 | 1.53k | if (type == '(') { |
431 | 753 | return readPolygonText(tokenizer, ordinateFlags); |
432 | 753 | } |
433 | | |
434 | 784 | GeometryTypeId defaultType = GEOS_POLYGON; |
435 | 784 | auto component = readGeometryTaggedText(tokenizer, ordinateFlags, &defaultType); |
436 | 784 | if (dynamic_cast<Surface*>(component.get())) { |
437 | 595 | return component; |
438 | 595 | } |
439 | | |
440 | 189 | throw ParseException("Expected POLYGON or CURVEPOLYGON but got " + component->getGeometryType()); |
441 | 784 | } |
442 | | |
443 | | std::unique_ptr<CompoundCurve> |
444 | | WKTReader::readCompoundCurveText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
445 | 1.16k | { |
446 | 1.16k | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
447 | 1.16k | if (nextToken == "EMPTY") { |
448 | 5 | return geometryFactory->createCompoundCurve(); |
449 | 5 | } |
450 | | |
451 | 1.15k | std::vector<std::unique_ptr<SimpleCurve>> curves; |
452 | 2.61k | do { |
453 | 2.61k | auto curve = readCurveText(tokenizer, ordinateFlags); |
454 | 2.61k | if (dynamic_cast<SimpleCurve*>(curve.get())) { |
455 | 2.48k | curves.emplace_back(static_cast<SimpleCurve*>(curve.release())); |
456 | 2.48k | } else { |
457 | 135 | throw ParseException("Expected LINESTRING or CIRCULARSTRING but got " + curve->getGeometryType()); |
458 | 135 | } |
459 | 2.48k | nextToken = getNextCloserOrComma(tokenizer); |
460 | 2.48k | } while (nextToken == ","); |
461 | | |
462 | 1.02k | return geometryFactory->createCompoundCurve(std::move(curves)); |
463 | 1.15k | } |
464 | | |
465 | | std::unique_ptr<MultiPoint> |
466 | | WKTReader::readMultiPointText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
467 | 18.6k | { |
468 | 18.6k | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
469 | 18.6k | if(nextToken == "EMPTY") { |
470 | 690 | return geometryFactory->createMultiPoint(); |
471 | 690 | } |
472 | | |
473 | 17.9k | int tok = tokenizer->peekNextToken(); |
474 | | |
475 | 17.9k | if(tok == StringTokenizer::TT_NUMBER) { |
476 | | // Try to parse "MULTIPOINT (0 0, 1 1)" |
477 | 16.3k | auto coords = detail::make_unique<CoordinateSequence>(0u, ordinateFlags.hasZ(), ordinateFlags.hasM()); |
478 | | |
479 | 16.3k | CoordinateXYZM coord(0, 0, DoubleNotANumber, DoubleNotANumber); |
480 | 3.95M | do { |
481 | 3.95M | getPreciseCoordinate(tokenizer, ordinateFlags, coord); |
482 | 3.95M | coords->add(coord); |
483 | 3.95M | nextToken = getNextCloserOrComma(tokenizer); |
484 | 3.95M | } |
485 | 3.95M | while(nextToken == ","); |
486 | | |
487 | 16.3k | return std::unique_ptr<MultiPoint>(geometryFactory->createMultiPoint(*coords)); |
488 | 16.3k | } |
489 | | |
490 | 1.61k | else if(tok == '(' || // Try to parse "MULTIPOINT ((0 0), (1 1))" |
491 | 92 | tok == StringTokenizer::TT_WORD) // "MULTIPOINT (EMPTY, (1 1))" |
492 | 1.60k | { |
493 | 1.60k | std::vector<std::unique_ptr<Point>> points; |
494 | | |
495 | 2.75k | do { |
496 | 2.75k | points.push_back(readPointText(tokenizer, ordinateFlags)); |
497 | 2.75k | nextToken = getNextCloserOrComma(tokenizer); |
498 | 2.75k | } while(nextToken == ","); |
499 | | |
500 | 1.60k | return geometryFactory->createMultiPoint(std::move(points)); |
501 | 1.60k | } |
502 | | |
503 | 6 | else { |
504 | 6 | std::stringstream err; |
505 | 6 | err << "Unexpected token: "; |
506 | 6 | switch(tok) { |
507 | 0 | case StringTokenizer::TT_WORD: |
508 | 0 | err << "WORD " << tokenizer->getSVal(); |
509 | 0 | break; |
510 | 0 | case StringTokenizer::TT_NUMBER: |
511 | 0 | err << "NUMBER " << tokenizer->getNVal(); |
512 | 0 | break; |
513 | 2 | case StringTokenizer::TT_EOF: |
514 | 2 | case StringTokenizer::TT_EOL: |
515 | 2 | err << "EOF or EOL"; |
516 | 2 | break; |
517 | 0 | case '(': |
518 | 0 | err << "("; |
519 | 0 | break; |
520 | 1 | case ')': |
521 | 1 | err << ")"; |
522 | 1 | break; |
523 | 1 | case ',': |
524 | 1 | err << ","; |
525 | 1 | break; |
526 | 0 | default: |
527 | 0 | err << "??"; |
528 | 0 | break; |
529 | 6 | } |
530 | 4 | err << std::endl; |
531 | 4 | throw ParseException(err.str()); |
532 | 6 | } |
533 | 17.9k | } |
534 | | |
535 | | std::unique_ptr<Polygon> |
536 | | WKTReader::readPolygonText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
537 | 1.70M | { |
538 | 1.70M | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
539 | 1.70M | if(nextToken == "EMPTY") { |
540 | 996 | auto coords = detail::make_unique<CoordinateSequence>(0u, ordinateFlags.hasZ(), ordinateFlags.hasM()); |
541 | 996 | auto ring = geometryFactory->createLinearRing(std::move(coords)); |
542 | 996 | return geometryFactory->createPolygon(std::move(ring)); |
543 | 996 | } |
544 | | |
545 | 1.70M | std::vector<std::unique_ptr<LinearRing>> holes; |
546 | 1.70M | auto shell = readLinearRingText(tokenizer, ordinateFlags); |
547 | 1.70M | nextToken = getNextCloserOrComma(tokenizer); |
548 | 2.21M | while(nextToken == ",") { |
549 | 513k | holes.push_back(readLinearRingText(tokenizer, ordinateFlags)); |
550 | 513k | nextToken = getNextCloserOrComma(tokenizer); |
551 | 513k | } |
552 | | |
553 | 1.70M | return geometryFactory->createPolygon(std::move(shell), std::move(holes)); |
554 | 1.70M | } |
555 | | |
556 | | std::unique_ptr<CurvePolygon> |
557 | | WKTReader::readCurvePolygonText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
558 | 611 | { |
559 | 611 | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
560 | 611 | if(nextToken == "EMPTY") { |
561 | 9 | auto coords = detail::make_unique<CoordinateSequence>(0u, ordinateFlags.hasZ(), ordinateFlags.hasM()); |
562 | 9 | std::unique_ptr<Curve> ring = geometryFactory->createLinearRing(std::move(coords)); |
563 | 9 | return geometryFactory->createCurvePolygon(std::move(ring)); |
564 | 9 | } |
565 | | |
566 | 602 | std::vector<std::unique_ptr<Curve>> holes; |
567 | 602 | auto shell = readCurveText(tokenizer, ordinateFlags); |
568 | 602 | nextToken = getNextCloserOrComma(tokenizer); |
569 | 4.61k | while(nextToken == ",") { |
570 | 4.00k | holes.push_back(readCurveText(tokenizer, ordinateFlags)); |
571 | 4.00k | nextToken = getNextCloserOrComma(tokenizer); |
572 | 4.00k | } |
573 | | |
574 | 602 | return geometryFactory->createCurvePolygon(std::move(shell), std::move(holes)); |
575 | 611 | } |
576 | | |
577 | | std::unique_ptr<MultiLineString> |
578 | | WKTReader::readMultiLineStringText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
579 | 418 | { |
580 | 418 | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
581 | 418 | if(nextToken == "EMPTY") { |
582 | 6 | return geometryFactory->createMultiLineString(); |
583 | 6 | } |
584 | | |
585 | 412 | std::vector<std::unique_ptr<LineString>> lineStrings; |
586 | 2.55k | do { |
587 | 2.55k | lineStrings.push_back(readLineStringText(tokenizer, ordinateFlags)); |
588 | 2.55k | nextToken = getNextCloserOrComma(tokenizer); |
589 | 2.55k | } while (nextToken == ","); |
590 | | |
591 | 412 | return geometryFactory->createMultiLineString(std::move(lineStrings)); |
592 | 418 | } |
593 | | |
594 | | std::unique_ptr<MultiCurve> |
595 | | WKTReader::readMultiCurveText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
596 | 374 | { |
597 | 374 | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
598 | 374 | if(nextToken == "EMPTY") { |
599 | 1 | return geometryFactory->createMultiCurve(); |
600 | 1 | } |
601 | | |
602 | 373 | std::vector<std::unique_ptr<Curve>> curves; |
603 | 2.82k | do { |
604 | 2.82k | curves.push_back(readCurveText(tokenizer, ordinateFlags)); |
605 | 2.82k | nextToken = getNextCloserOrComma(tokenizer); |
606 | 2.82k | } while(nextToken == ","); |
607 | | |
608 | 373 | return geometryFactory->createMultiCurve(std::move(curves)); |
609 | 374 | } |
610 | | |
611 | | std::unique_ptr<MultiPolygon> |
612 | | WKTReader::readMultiPolygonText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
613 | 1.06k | { |
614 | 1.06k | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
615 | 1.06k | if(nextToken == "EMPTY") { |
616 | 96 | return geometryFactory->createMultiPolygon(); |
617 | 96 | } |
618 | | |
619 | 970 | std::vector<std::unique_ptr<Polygon>> polygons; |
620 | 1.70M | do { |
621 | 1.70M | polygons.push_back(readPolygonText(tokenizer, ordinateFlags)); |
622 | 1.70M | nextToken = getNextCloserOrComma(tokenizer); |
623 | 1.70M | } while(nextToken == ","); |
624 | | |
625 | 970 | return geometryFactory->createMultiPolygon(std::move(polygons)); |
626 | 1.06k | } |
627 | | |
628 | | std::unique_ptr<MultiSurface> |
629 | | WKTReader::readMultiSurfaceText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
630 | 237 | { |
631 | 237 | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
632 | 237 | if(nextToken == "EMPTY") { |
633 | 1 | return geometryFactory->createMultiSurface(); |
634 | 1 | } |
635 | | |
636 | 236 | std::vector<std::unique_ptr<Geometry>> surfaces; |
637 | 1.53k | do { |
638 | 1.53k | surfaces.push_back(readSurfaceText(tokenizer, ordinateFlags)); |
639 | 1.53k | nextToken = getNextCloserOrComma(tokenizer); |
640 | 1.53k | } while(nextToken == ","); |
641 | | |
642 | 236 | return geometryFactory->createMultiSurface(std::move(surfaces)); |
643 | 237 | } |
644 | | |
645 | | std::unique_ptr<GeometryCollection> |
646 | | WKTReader::readGeometryCollectionText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const |
647 | 2.98k | { |
648 | 2.98k | std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); |
649 | 2.98k | if(nextToken == "EMPTY") { |
650 | 648 | return geometryFactory->createGeometryCollection(); |
651 | 648 | } |
652 | | |
653 | 2.33k | std::vector<std::unique_ptr<Geometry>> geoms; |
654 | 26.1k | do { |
655 | 26.1k | geoms.push_back(readGeometryTaggedText(tokenizer, ordinateFlags)); |
656 | 26.1k | nextToken = getNextCloserOrComma(tokenizer); |
657 | 26.1k | } while(nextToken == ","); |
658 | | |
659 | 2.33k | return geometryFactory->createGeometryCollection(std::move(geoms)); |
660 | 2.98k | } |
661 | | |
662 | | } // namespace geos.io |
663 | | } // namespace geos |