/src/gdal/ogr/gml2ogrgeometry.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GML Reader |
4 | | * Purpose: Code to translate between GML and OGR geometry forms. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2002, Frank Warmerdam |
9 | | * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ***************************************************************************** |
13 | | * |
14 | | * Independent Security Audit 2003/04/17 Andrey Kiselev: |
15 | | * Completed audit of this module. All functions may be used without buffer |
16 | | * overflows and stack corruptions with any kind of input data. |
17 | | * |
18 | | * Security Audit 2003/03/28 warmerda: |
19 | | * Completed security audit. I believe that this module may be safely used |
20 | | * to parse, arbitrary GML potentially provided by a hostile source without |
21 | | * compromising the system. |
22 | | * |
23 | | */ |
24 | | |
25 | | #include "cpl_port.h" |
26 | | #include "ogr_api.h" |
27 | | |
28 | | #include <algorithm> |
29 | | #include <cassert> |
30 | | #include <cctype> |
31 | | #include <cmath> |
32 | | #include <cstdlib> |
33 | | #include <cstring> |
34 | | |
35 | | #include "cpl_conv.h" |
36 | | #include "cpl_error.h" |
37 | | #include "cpl_minixml.h" |
38 | | #include "cpl_string.h" |
39 | | #include "ogr_core.h" |
40 | | #include "ogr_geometry.h" |
41 | | #include "ogr_p.h" |
42 | | #include "ogr_spatialref.h" |
43 | | #include "ogr_srs_api.h" |
44 | | #include "ogr_geo_utils.h" |
45 | | |
46 | | constexpr double kdfD2R = M_PI / 180.0; |
47 | | constexpr double kdf2PI = 2.0 * M_PI; |
48 | | |
49 | | /************************************************************************/ |
50 | | /* GMLGetCoordTokenPos() */ |
51 | | /************************************************************************/ |
52 | | |
53 | | static const char *GMLGetCoordTokenPos(const char *pszStr, |
54 | | const char **ppszNextToken) |
55 | 0 | { |
56 | 0 | char ch; |
57 | 0 | while (true) |
58 | 0 | { |
59 | | // cppcheck-suppress nullPointerRedundantCheck |
60 | 0 | ch = *pszStr; |
61 | 0 | if (ch == '\0') |
62 | 0 | { |
63 | 0 | *ppszNextToken = pszStr; |
64 | 0 | return nullptr; |
65 | 0 | } |
66 | 0 | else if (!(ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || |
67 | 0 | ch == ',')) |
68 | 0 | break; |
69 | 0 | pszStr++; |
70 | 0 | } |
71 | | |
72 | 0 | const char *pszToken = pszStr; |
73 | 0 | while ((ch = *pszStr) != '\0') |
74 | 0 | { |
75 | 0 | if (ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',') |
76 | 0 | { |
77 | 0 | *ppszNextToken = pszStr; |
78 | 0 | return pszToken; |
79 | 0 | } |
80 | 0 | pszStr++; |
81 | 0 | } |
82 | 0 | *ppszNextToken = pszStr; |
83 | 0 | return pszToken; |
84 | 0 | } |
85 | | |
86 | | /************************************************************************/ |
87 | | /* BareGMLElement() */ |
88 | | /* */ |
89 | | /* Returns the passed string with any namespace prefix */ |
90 | | /* stripped off. */ |
91 | | /************************************************************************/ |
92 | | |
93 | | static const char *BareGMLElement(const char *pszInput) |
94 | | |
95 | 0 | { |
96 | 0 | const char *pszReturn = strchr(pszInput, ':'); |
97 | 0 | if (pszReturn == nullptr) |
98 | 0 | pszReturn = pszInput; |
99 | 0 | else |
100 | 0 | pszReturn++; |
101 | |
|
102 | 0 | return pszReturn; |
103 | 0 | } |
104 | | |
105 | | /************************************************************************/ |
106 | | /* FindBareXMLChild() */ |
107 | | /* */ |
108 | | /* Find a child node with the indicated "bare" name, that is */ |
109 | | /* after any namespace qualifiers have been stripped off. */ |
110 | | /************************************************************************/ |
111 | | |
112 | | static const CPLXMLNode *FindBareXMLChild(const CPLXMLNode *psParent, |
113 | | const char *pszBareName) |
114 | | |
115 | 0 | { |
116 | 0 | const CPLXMLNode *psCandidate = psParent->psChild; |
117 | |
|
118 | 0 | while (psCandidate != nullptr) |
119 | 0 | { |
120 | 0 | if (psCandidate->eType == CXT_Element && |
121 | 0 | EQUAL(BareGMLElement(psCandidate->pszValue), pszBareName)) |
122 | 0 | return psCandidate; |
123 | | |
124 | 0 | psCandidate = psCandidate->psNext; |
125 | 0 | } |
126 | | |
127 | 0 | return nullptr; |
128 | 0 | } |
129 | | |
130 | | /************************************************************************/ |
131 | | /* GetElementText() */ |
132 | | /************************************************************************/ |
133 | | |
134 | | static const char *GetElementText(const CPLXMLNode *psElement) |
135 | | |
136 | 0 | { |
137 | 0 | if (psElement == nullptr) |
138 | 0 | return nullptr; |
139 | | |
140 | 0 | const CPLXMLNode *psChild = psElement->psChild; |
141 | |
|
142 | 0 | while (psChild != nullptr) |
143 | 0 | { |
144 | 0 | if (psChild->eType == CXT_Text) |
145 | 0 | return psChild->pszValue; |
146 | | |
147 | 0 | psChild = psChild->psNext; |
148 | 0 | } |
149 | | |
150 | 0 | return nullptr; |
151 | 0 | } |
152 | | |
153 | | /************************************************************************/ |
154 | | /* GetChildElement() */ |
155 | | /************************************************************************/ |
156 | | |
157 | | static const CPLXMLNode *GetChildElement(const CPLXMLNode *psElement) |
158 | | |
159 | 0 | { |
160 | 0 | if (psElement == nullptr) |
161 | 0 | return nullptr; |
162 | | |
163 | 0 | const CPLXMLNode *psChild = psElement->psChild; |
164 | |
|
165 | 0 | while (psChild != nullptr) |
166 | 0 | { |
167 | 0 | if (psChild->eType == CXT_Element) |
168 | 0 | return psChild; |
169 | | |
170 | 0 | psChild = psChild->psNext; |
171 | 0 | } |
172 | | |
173 | 0 | return nullptr; |
174 | 0 | } |
175 | | |
176 | | /************************************************************************/ |
177 | | /* GetElementOrientation() */ |
178 | | /* Returns true for positive orientation. */ |
179 | | /************************************************************************/ |
180 | | |
181 | | static bool GetElementOrientation(const CPLXMLNode *psElement) |
182 | 0 | { |
183 | 0 | if (psElement == nullptr) |
184 | 0 | return true; |
185 | | |
186 | 0 | const CPLXMLNode *psChild = psElement->psChild; |
187 | |
|
188 | 0 | while (psChild != nullptr) |
189 | 0 | { |
190 | 0 | if (psChild->eType == CXT_Attribute && |
191 | 0 | EQUAL(psChild->pszValue, "orientation")) |
192 | 0 | return EQUAL(psChild->psChild->pszValue, "+"); |
193 | | |
194 | 0 | psChild = psChild->psNext; |
195 | 0 | } |
196 | | |
197 | 0 | return true; |
198 | 0 | } |
199 | | |
200 | | /************************************************************************/ |
201 | | /* AddPoint() */ |
202 | | /* */ |
203 | | /* Add a point to the passed geometry. */ |
204 | | /************************************************************************/ |
205 | | |
206 | | static bool AddPoint(OGRGeometry *poGeometry, double dfX, double dfY, |
207 | | double dfZ, int nDimension) |
208 | | |
209 | 0 | { |
210 | 0 | const OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType()); |
211 | 0 | if (eType == wkbPoint) |
212 | 0 | { |
213 | 0 | OGRPoint *poPoint = poGeometry->toPoint(); |
214 | |
|
215 | 0 | if (!poPoint->IsEmpty()) |
216 | 0 | { |
217 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
218 | 0 | "More than one coordinate for <Point> element."); |
219 | 0 | return false; |
220 | 0 | } |
221 | | |
222 | 0 | poPoint->setX(dfX); |
223 | 0 | poPoint->setY(dfY); |
224 | 0 | if (nDimension == 3) |
225 | 0 | poPoint->setZ(dfZ); |
226 | |
|
227 | 0 | return true; |
228 | 0 | } |
229 | 0 | else if (eType == wkbLineString || eType == wkbCircularString) |
230 | 0 | { |
231 | 0 | OGRSimpleCurve *poCurve = poGeometry->toSimpleCurve(); |
232 | 0 | if (nDimension == 3) |
233 | 0 | poCurve->addPoint(dfX, dfY, dfZ); |
234 | 0 | else |
235 | 0 | poCurve->addPoint(dfX, dfY); |
236 | |
|
237 | 0 | return true; |
238 | 0 | } |
239 | | |
240 | 0 | CPLAssert(false); |
241 | 0 | return false; |
242 | 0 | } |
243 | | |
244 | | /************************************************************************/ |
245 | | /* ParseGMLCoordinates() */ |
246 | | /************************************************************************/ |
247 | | |
248 | | static bool ParseGMLCoordinates(const CPLXMLNode *psGeomNode, |
249 | | OGRGeometry *poGeometry, int nSRSDimension) |
250 | | |
251 | 0 | { |
252 | 0 | const CPLXMLNode *psCoordinates = |
253 | 0 | FindBareXMLChild(psGeomNode, "coordinates"); |
254 | | |
255 | | /* -------------------------------------------------------------------- */ |
256 | | /* Handle <coordinates> case. */ |
257 | | /* Note that we don't do a strict validation, so we accept and */ |
258 | | /* sometimes generate output whereas we should just reject it. */ |
259 | | /* -------------------------------------------------------------------- */ |
260 | 0 | if (psCoordinates != nullptr) |
261 | 0 | { |
262 | 0 | const char *pszCoordString = GetElementText(psCoordinates); |
263 | |
|
264 | 0 | const char *pszDecimal = |
265 | 0 | CPLGetXMLValue(psCoordinates, "decimal", nullptr); |
266 | 0 | char chDecimal = '.'; |
267 | 0 | if (pszDecimal != nullptr) |
268 | 0 | { |
269 | 0 | if (strlen(pszDecimal) != 1 || |
270 | 0 | (pszDecimal[0] >= '0' && pszDecimal[0] <= '9')) |
271 | 0 | { |
272 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
273 | 0 | "Wrong value for decimal attribute"); |
274 | 0 | return false; |
275 | 0 | } |
276 | 0 | chDecimal = pszDecimal[0]; |
277 | 0 | } |
278 | | |
279 | 0 | const char *pszCS = CPLGetXMLValue(psCoordinates, "cs", nullptr); |
280 | 0 | char chCS = ','; |
281 | 0 | if (pszCS != nullptr) |
282 | 0 | { |
283 | 0 | if (strlen(pszCS) != 1 || (pszCS[0] >= '0' && pszCS[0] <= '9')) |
284 | 0 | { |
285 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
286 | 0 | "Wrong value for cs attribute"); |
287 | 0 | return false; |
288 | 0 | } |
289 | 0 | chCS = pszCS[0]; |
290 | 0 | } |
291 | 0 | const char *pszTS = CPLGetXMLValue(psCoordinates, "ts", nullptr); |
292 | 0 | char chTS = ' '; |
293 | 0 | if (pszTS != nullptr) |
294 | 0 | { |
295 | 0 | if (strlen(pszTS) != 1 || (pszTS[0] >= '0' && pszTS[0] <= '9')) |
296 | 0 | { |
297 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
298 | 0 | "Wrong value for ts attribute"); |
299 | 0 | return false; |
300 | 0 | } |
301 | 0 | chTS = pszTS[0]; |
302 | 0 | } |
303 | | |
304 | 0 | if (pszCoordString == nullptr) |
305 | 0 | { |
306 | 0 | poGeometry->empty(); |
307 | 0 | return true; |
308 | 0 | } |
309 | | |
310 | | // Skip leading whitespace. See |
311 | | // https://github.com/OSGeo/gdal/issues/5494 |
312 | 0 | while (*pszCoordString != '\0' && |
313 | 0 | isspace(static_cast<unsigned char>(*pszCoordString))) |
314 | 0 | { |
315 | 0 | pszCoordString++; |
316 | 0 | } |
317 | |
|
318 | 0 | int iCoord = 0; |
319 | 0 | const OGRwkbGeometryType eType = |
320 | 0 | wkbFlatten(poGeometry->getGeometryType()); |
321 | 0 | OGRSimpleCurve *poCurve = |
322 | 0 | (eType == wkbLineString || eType == wkbCircularString) |
323 | 0 | ? poGeometry->toSimpleCurve() |
324 | 0 | : nullptr; |
325 | 0 | for (int iter = (eType == wkbPoint ? 1 : 0); iter < 2; iter++) |
326 | 0 | { |
327 | 0 | const char *pszStr = pszCoordString; |
328 | 0 | double dfX = 0; |
329 | 0 | double dfY = 0; |
330 | 0 | iCoord = 0; |
331 | 0 | while (*pszStr != '\0') |
332 | 0 | { |
333 | 0 | int nDimension = 2; |
334 | | // parse out 2 or 3 tuple. |
335 | 0 | if (iter == 1) |
336 | 0 | { |
337 | 0 | if (chDecimal == '.') |
338 | 0 | dfX = OGRFastAtof(pszStr); |
339 | 0 | else |
340 | 0 | dfX = CPLAtofDelim(pszStr, chDecimal); |
341 | 0 | } |
342 | 0 | while (*pszStr != '\0' && *pszStr != chCS && |
343 | 0 | !isspace(static_cast<unsigned char>(*pszStr))) |
344 | 0 | pszStr++; |
345 | |
|
346 | 0 | if (*pszStr == '\0') |
347 | 0 | { |
348 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
349 | 0 | "Corrupt <coordinates> value."); |
350 | 0 | return false; |
351 | 0 | } |
352 | 0 | else if (chCS == ',' && pszCS == nullptr && |
353 | 0 | isspace(static_cast<unsigned char>(*pszStr))) |
354 | 0 | { |
355 | | // In theory, the coordinates inside a coordinate tuple |
356 | | // should be separated by a comma. However it has been found |
357 | | // in the wild that the coordinates are in rare cases |
358 | | // separated by a space, and the tuples by a comma. See: |
359 | | // https://52north.org/twiki/bin/view/Processing/WPS-IDWExtension-ObservationCollectionExample |
360 | | // or |
361 | | // http://agisdemo.faa.gov/aixmServices/getAllFeaturesByLocatorId?locatorId=DFW |
362 | 0 | chCS = ' '; |
363 | 0 | chTS = ','; |
364 | 0 | } |
365 | | |
366 | 0 | pszStr++; |
367 | |
|
368 | 0 | if (iter == 1) |
369 | 0 | { |
370 | 0 | if (chDecimal == '.') |
371 | 0 | dfY = OGRFastAtof(pszStr); |
372 | 0 | else |
373 | 0 | dfY = CPLAtofDelim(pszStr, chDecimal); |
374 | 0 | } |
375 | 0 | while (*pszStr != '\0' && *pszStr != chCS && *pszStr != chTS && |
376 | 0 | !isspace(static_cast<unsigned char>(*pszStr))) |
377 | 0 | pszStr++; |
378 | |
|
379 | 0 | double dfZ = 0.0; |
380 | 0 | if (*pszStr == chCS) |
381 | 0 | { |
382 | 0 | pszStr++; |
383 | 0 | if (iter == 1) |
384 | 0 | { |
385 | 0 | if (chDecimal == '.') |
386 | 0 | dfZ = OGRFastAtof(pszStr); |
387 | 0 | else |
388 | 0 | dfZ = CPLAtofDelim(pszStr, chDecimal); |
389 | 0 | } |
390 | 0 | nDimension = 3; |
391 | 0 | while (*pszStr != '\0' && *pszStr != chCS && |
392 | 0 | *pszStr != chTS && |
393 | 0 | !isspace(static_cast<unsigned char>(*pszStr))) |
394 | 0 | pszStr++; |
395 | 0 | } |
396 | |
|
397 | 0 | if (*pszStr == chTS) |
398 | 0 | { |
399 | 0 | pszStr++; |
400 | 0 | } |
401 | |
|
402 | 0 | while (isspace(static_cast<unsigned char>(*pszStr))) |
403 | 0 | pszStr++; |
404 | |
|
405 | 0 | if (iter == 1) |
406 | 0 | { |
407 | 0 | if (poCurve) |
408 | 0 | { |
409 | 0 | if (nDimension == 3) |
410 | 0 | poCurve->setPoint(iCoord, dfX, dfY, dfZ); |
411 | 0 | else |
412 | 0 | poCurve->setPoint(iCoord, dfX, dfY); |
413 | 0 | } |
414 | 0 | else if (!AddPoint(poGeometry, dfX, dfY, dfZ, nDimension)) |
415 | 0 | return false; |
416 | 0 | } |
417 | | |
418 | 0 | iCoord++; |
419 | 0 | } |
420 | | |
421 | 0 | if (poCurve && iter == 0) |
422 | 0 | { |
423 | 0 | poCurve->setNumPoints(iCoord); |
424 | 0 | } |
425 | 0 | } |
426 | | |
427 | 0 | return iCoord > 0; |
428 | 0 | } |
429 | | |
430 | | /* -------------------------------------------------------------------- */ |
431 | | /* Is this a "pos"? GML 3 construct. */ |
432 | | /* Parse if it exist a series of pos elements (this would allow */ |
433 | | /* the correct parsing of gml3.1.1 geometries such as linestring */ |
434 | | /* defined with pos elements. */ |
435 | | /* -------------------------------------------------------------------- */ |
436 | 0 | bool bHasFoundPosElement = false; |
437 | 0 | for (const CPLXMLNode *psPos = psGeomNode->psChild; psPos != nullptr; |
438 | 0 | psPos = psPos->psNext) |
439 | 0 | { |
440 | 0 | if (psPos->eType != CXT_Element) |
441 | 0 | continue; |
442 | | |
443 | 0 | const char *pszSubElement = BareGMLElement(psPos->pszValue); |
444 | |
|
445 | 0 | if (EQUAL(pszSubElement, "pointProperty")) |
446 | 0 | { |
447 | 0 | for (const CPLXMLNode *psPointPropertyIter = psPos->psChild; |
448 | 0 | psPointPropertyIter != nullptr; |
449 | 0 | psPointPropertyIter = psPointPropertyIter->psNext) |
450 | 0 | { |
451 | 0 | if (psPointPropertyIter->eType != CXT_Element) |
452 | 0 | continue; |
453 | | |
454 | 0 | const char *pszBareElement = |
455 | 0 | BareGMLElement(psPointPropertyIter->pszValue); |
456 | 0 | if (EQUAL(pszBareElement, "Point") || |
457 | 0 | EQUAL(pszBareElement, "ElevatedPoint")) |
458 | 0 | { |
459 | 0 | OGRPoint oPoint; |
460 | 0 | if (ParseGMLCoordinates(psPointPropertyIter, &oPoint, |
461 | 0 | nSRSDimension)) |
462 | 0 | { |
463 | 0 | const bool bSuccess = AddPoint( |
464 | 0 | poGeometry, oPoint.getX(), oPoint.getY(), |
465 | 0 | oPoint.getZ(), oPoint.getCoordinateDimension()); |
466 | 0 | if (bSuccess) |
467 | 0 | bHasFoundPosElement = true; |
468 | 0 | else |
469 | 0 | return false; |
470 | 0 | } |
471 | 0 | } |
472 | 0 | } |
473 | | |
474 | 0 | if (psPos->psChild && psPos->psChild->eType == CXT_Attribute && |
475 | 0 | psPos->psChild->psNext == nullptr && |
476 | 0 | strcmp(psPos->psChild->pszValue, "xlink:href") == 0) |
477 | 0 | { |
478 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
479 | 0 | "Cannot resolve xlink:href='%s'. " |
480 | 0 | "Try setting GML_SKIP_RESOLVE_ELEMS=NONE", |
481 | 0 | psPos->psChild->psChild->pszValue); |
482 | 0 | } |
483 | |
|
484 | 0 | continue; |
485 | 0 | } |
486 | | |
487 | 0 | if (!EQUAL(pszSubElement, "pos")) |
488 | 0 | continue; |
489 | | |
490 | 0 | const char *pszPos = GetElementText(psPos); |
491 | 0 | if (pszPos == nullptr) |
492 | 0 | { |
493 | 0 | poGeometry->empty(); |
494 | 0 | return true; |
495 | 0 | } |
496 | | |
497 | 0 | const char *pszCur = pszPos; |
498 | 0 | const char *pszX = GMLGetCoordTokenPos(pszCur, &pszCur); |
499 | 0 | const char *pszY = (pszCur[0] != '\0') |
500 | 0 | ? GMLGetCoordTokenPos(pszCur, &pszCur) |
501 | 0 | : nullptr; |
502 | 0 | const char *pszZ = (pszCur[0] != '\0') |
503 | 0 | ? GMLGetCoordTokenPos(pszCur, &pszCur) |
504 | 0 | : nullptr; |
505 | |
|
506 | 0 | if (pszY == nullptr) |
507 | 0 | { |
508 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
509 | 0 | "Did not get 2+ values in <gml:pos>%s</gml:pos> tuple.", |
510 | 0 | pszPos); |
511 | 0 | return false; |
512 | 0 | } |
513 | | |
514 | 0 | const double dfX = OGRFastAtof(pszX); |
515 | 0 | const double dfY = OGRFastAtof(pszY); |
516 | 0 | const double dfZ = (pszZ != nullptr) ? OGRFastAtof(pszZ) : 0.0; |
517 | 0 | const bool bSuccess = |
518 | 0 | AddPoint(poGeometry, dfX, dfY, dfZ, (pszZ != nullptr) ? 3 : 2); |
519 | |
|
520 | 0 | if (bSuccess) |
521 | 0 | bHasFoundPosElement = true; |
522 | 0 | else |
523 | 0 | return false; |
524 | 0 | } |
525 | | |
526 | 0 | if (bHasFoundPosElement) |
527 | 0 | return true; |
528 | | |
529 | | /* -------------------------------------------------------------------- */ |
530 | | /* Is this a "posList"? GML 3 construct (SF profile). */ |
531 | | /* -------------------------------------------------------------------- */ |
532 | 0 | const CPLXMLNode *psPosList = FindBareXMLChild(psGeomNode, "posList"); |
533 | |
|
534 | 0 | if (psPosList != nullptr) |
535 | 0 | { |
536 | 0 | int nDimension = 2; |
537 | | |
538 | | // Try to detect the presence of an srsDimension attribute |
539 | | // This attribute is only available for gml3.1.1 but not |
540 | | // available for gml3.1 SF. |
541 | 0 | const char *pszSRSDimension = |
542 | 0 | CPLGetXMLValue(psPosList, "srsDimension", nullptr); |
543 | | // If not found at the posList level, try on the enclosing element. |
544 | 0 | if (pszSRSDimension == nullptr) |
545 | 0 | pszSRSDimension = |
546 | 0 | CPLGetXMLValue(psGeomNode, "srsDimension", nullptr); |
547 | 0 | if (pszSRSDimension != nullptr) |
548 | 0 | nDimension = atoi(pszSRSDimension); |
549 | 0 | else if (nSRSDimension != 0) |
550 | | // Or use one coming from a still higher level element (#5606). |
551 | 0 | nDimension = nSRSDimension; |
552 | |
|
553 | 0 | if (nDimension != 2 && nDimension != 3) |
554 | 0 | { |
555 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
556 | 0 | "srsDimension = %d not supported", nDimension); |
557 | 0 | return false; |
558 | 0 | } |
559 | | |
560 | 0 | const char *pszPosList = GetElementText(psPosList); |
561 | 0 | if (pszPosList == nullptr) |
562 | 0 | { |
563 | 0 | poGeometry->empty(); |
564 | 0 | return true; |
565 | 0 | } |
566 | | |
567 | 0 | bool bSuccess = false; |
568 | 0 | const char *pszCur = pszPosList; |
569 | 0 | while (true) |
570 | 0 | { |
571 | 0 | const char *pszX = GMLGetCoordTokenPos(pszCur, &pszCur); |
572 | 0 | if (pszX == nullptr && bSuccess) |
573 | 0 | break; |
574 | 0 | const char *pszY = (pszCur[0] != '\0') |
575 | 0 | ? GMLGetCoordTokenPos(pszCur, &pszCur) |
576 | 0 | : nullptr; |
577 | 0 | const char *pszZ = (nDimension == 3 && pszCur[0] != '\0') |
578 | 0 | ? GMLGetCoordTokenPos(pszCur, &pszCur) |
579 | 0 | : nullptr; |
580 | |
|
581 | 0 | if (pszY == nullptr || (nDimension == 3 && pszZ == nullptr)) |
582 | 0 | { |
583 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
584 | 0 | "Did not get at least %d values or invalid number of " |
585 | 0 | "set of coordinates <gml:posList>%s</gml:posList>", |
586 | 0 | nDimension, pszPosList); |
587 | 0 | return false; |
588 | 0 | } |
589 | | |
590 | 0 | double dfX = OGRFastAtof(pszX); |
591 | 0 | double dfY = OGRFastAtof(pszY); |
592 | 0 | double dfZ = (pszZ != nullptr) ? OGRFastAtof(pszZ) : 0.0; |
593 | 0 | bSuccess = AddPoint(poGeometry, dfX, dfY, dfZ, nDimension); |
594 | |
|
595 | 0 | if (!bSuccess || pszCur == nullptr) |
596 | 0 | break; |
597 | 0 | } |
598 | | |
599 | 0 | return bSuccess; |
600 | 0 | } |
601 | | |
602 | | /* -------------------------------------------------------------------- */ |
603 | | /* Handle form with a list of <coord> items each with an <X>, */ |
604 | | /* and <Y> element. */ |
605 | | /* -------------------------------------------------------------------- */ |
606 | 0 | int iCoord = 0; |
607 | 0 | for (const CPLXMLNode *psCoordNode = psGeomNode->psChild; |
608 | 0 | psCoordNode != nullptr; psCoordNode = psCoordNode->psNext) |
609 | 0 | { |
610 | 0 | if (psCoordNode->eType != CXT_Element || |
611 | 0 | !EQUAL(BareGMLElement(psCoordNode->pszValue), "coord")) |
612 | 0 | continue; |
613 | | |
614 | 0 | const CPLXMLNode *psXNode = FindBareXMLChild(psCoordNode, "X"); |
615 | 0 | const CPLXMLNode *psYNode = FindBareXMLChild(psCoordNode, "Y"); |
616 | 0 | const CPLXMLNode *psZNode = FindBareXMLChild(psCoordNode, "Z"); |
617 | |
|
618 | 0 | if (psXNode == nullptr || psYNode == nullptr || |
619 | 0 | GetElementText(psXNode) == nullptr || |
620 | 0 | GetElementText(psYNode) == nullptr || |
621 | 0 | (psZNode != nullptr && GetElementText(psZNode) == nullptr)) |
622 | 0 | { |
623 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
624 | 0 | "Corrupt <coord> element, missing <X> or <Y> element?"); |
625 | 0 | return false; |
626 | 0 | } |
627 | | |
628 | 0 | double dfX = OGRFastAtof(GetElementText(psXNode)); |
629 | 0 | double dfY = OGRFastAtof(GetElementText(psYNode)); |
630 | |
|
631 | 0 | int nDimension = 2; |
632 | 0 | double dfZ = 0.0; |
633 | 0 | if (psZNode != nullptr && GetElementText(psZNode) != nullptr) |
634 | 0 | { |
635 | 0 | dfZ = OGRFastAtof(GetElementText(psZNode)); |
636 | 0 | nDimension = 3; |
637 | 0 | } |
638 | |
|
639 | 0 | if (!AddPoint(poGeometry, dfX, dfY, dfZ, nDimension)) |
640 | 0 | return false; |
641 | | |
642 | 0 | iCoord++; |
643 | 0 | } |
644 | | |
645 | 0 | return iCoord > 0; |
646 | 0 | } |
647 | | |
648 | | #ifdef HAVE_GEOS |
649 | | /************************************************************************/ |
650 | | /* GML2FaceExtRing() */ |
651 | | /* */ |
652 | | /* Identifies the "good" Polygon within the collection returned */ |
653 | | /* by GEOSPolygonize() */ |
654 | | /* short rationale: GEOSPolygonize() will possibly return a */ |
655 | | /* collection of many Polygons; only one is the "good" one, */ |
656 | | /* (including both exterior- and interior-rings) */ |
657 | | /* any other simply represents a single "hole", and should be */ |
658 | | /* consequently ignored at all. */ |
659 | | /************************************************************************/ |
660 | | |
661 | | static std::unique_ptr<OGRPolygon> GML2FaceExtRing(const OGRGeometry *poGeom) |
662 | | { |
663 | | const OGRGeometryCollection *poColl = |
664 | | dynamic_cast<const OGRGeometryCollection *>(poGeom); |
665 | | if (poColl == nullptr) |
666 | | { |
667 | | CPLError(CE_Fatal, CPLE_AppDefined, |
668 | | "dynamic_cast failed. Expected OGRGeometryCollection."); |
669 | | return nullptr; |
670 | | } |
671 | | |
672 | | const OGRPolygon *poPolygonExterior = nullptr; |
673 | | const OGRPolygon *poPolygonInterior = nullptr; |
674 | | int iExterior = 0; |
675 | | int iInterior = 0; |
676 | | |
677 | | for (const auto *poChild : *poColl) |
678 | | { |
679 | | // A collection of Polygons is expected to be found. |
680 | | if (wkbFlatten(poChild->getGeometryType()) == wkbPolygon) |
681 | | { |
682 | | const OGRPolygon *poPoly = poChild->toPolygon(); |
683 | | if (poPoly->getNumInteriorRings() > 0) |
684 | | { |
685 | | poPolygonExterior = poPoly; |
686 | | iExterior++; |
687 | | } |
688 | | else |
689 | | { |
690 | | poPolygonInterior = poPoly; |
691 | | iInterior++; |
692 | | } |
693 | | } |
694 | | else |
695 | | { |
696 | | return nullptr; |
697 | | } |
698 | | } |
699 | | |
700 | | if (poPolygonInterior && iExterior == 0 && iInterior == 1) |
701 | | { |
702 | | // There is a single Polygon within the collection. |
703 | | return std::unique_ptr<OGRPolygon>(poPolygonInterior->clone()); |
704 | | } |
705 | | else if (poPolygonExterior && iExterior == 1 && |
706 | | iInterior == poColl->getNumGeometries() - 1) |
707 | | { |
708 | | // Return the unique Polygon containing holes. |
709 | | return std::unique_ptr<OGRPolygon>(poPolygonExterior->clone()); |
710 | | } |
711 | | |
712 | | return nullptr; |
713 | | } |
714 | | #endif |
715 | | |
716 | | /************************************************************************/ |
717 | | /* GML2OGRGeometry_AddToCompositeCurve() */ |
718 | | /************************************************************************/ |
719 | | |
720 | | static bool |
721 | | GML2OGRGeometry_AddToCompositeCurve(OGRCompoundCurve *poCC, |
722 | | std::unique_ptr<OGRGeometry> poGeom, |
723 | | bool &bChildrenAreAllLineString) |
724 | 0 | { |
725 | 0 | if (poGeom == nullptr || !OGR_GT_IsCurve(poGeom->getGeometryType())) |
726 | 0 | { |
727 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
728 | 0 | "CompositeCurve: Got %s geometry as Member instead of a " |
729 | 0 | "curve.", |
730 | 0 | poGeom ? poGeom->getGeometryName() : "NULL"); |
731 | 0 | return false; |
732 | 0 | } |
733 | | |
734 | | // Crazy but allowed by GML: composite in composite. |
735 | 0 | if (wkbFlatten(poGeom->getGeometryType()) == wkbCompoundCurve) |
736 | 0 | { |
737 | 0 | auto poCCChild = std::unique_ptr<OGRCompoundCurve>( |
738 | 0 | poGeom.release()->toCompoundCurve()); |
739 | 0 | while (poCCChild->getNumCurves() != 0) |
740 | 0 | { |
741 | 0 | auto poCurve = std::unique_ptr<OGRCurve>(poCCChild->stealCurve(0)); |
742 | 0 | if (wkbFlatten(poCurve->getGeometryType()) != wkbLineString) |
743 | 0 | bChildrenAreAllLineString = false; |
744 | 0 | if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE) |
745 | 0 | { |
746 | 0 | return false; |
747 | 0 | } |
748 | 0 | } |
749 | 0 | } |
750 | 0 | else |
751 | 0 | { |
752 | 0 | if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString) |
753 | 0 | bChildrenAreAllLineString = false; |
754 | |
|
755 | 0 | auto poCurve = std::unique_ptr<OGRCurve>(poGeom.release()->toCurve()); |
756 | 0 | if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE) |
757 | 0 | { |
758 | 0 | return false; |
759 | 0 | } |
760 | 0 | } |
761 | 0 | return true; |
762 | 0 | } |
763 | | |
764 | | /************************************************************************/ |
765 | | /* GML2OGRGeometry_AddToMultiSurface() */ |
766 | | /************************************************************************/ |
767 | | |
768 | | static bool GML2OGRGeometry_AddToMultiSurface( |
769 | | OGRMultiSurface *poMS, std::unique_ptr<OGRGeometry> poGeom, |
770 | | const char *pszMemberElement, bool &bChildrenAreAllPolygons) |
771 | 0 | { |
772 | 0 | if (poGeom == nullptr) |
773 | 0 | { |
774 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s", pszMemberElement); |
775 | 0 | return false; |
776 | 0 | } |
777 | | |
778 | 0 | OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType()); |
779 | 0 | if (eType == wkbPolygon || eType == wkbCurvePolygon) |
780 | 0 | { |
781 | 0 | if (eType != wkbPolygon) |
782 | 0 | bChildrenAreAllPolygons = false; |
783 | |
|
784 | 0 | if (poMS->addGeometry(std::move(poGeom)) != OGRERR_NONE) |
785 | 0 | { |
786 | 0 | return false; |
787 | 0 | } |
788 | 0 | } |
789 | 0 | else if (eType == wkbMultiPolygon || eType == wkbMultiSurface) |
790 | 0 | { |
791 | 0 | OGRMultiSurface *poMS2 = poGeom->toMultiSurface(); |
792 | 0 | for (int i = 0; i < poMS2->getNumGeometries(); i++) |
793 | 0 | { |
794 | 0 | if (wkbFlatten(poMS2->getGeometryRef(i)->getGeometryType()) != |
795 | 0 | wkbPolygon) |
796 | 0 | bChildrenAreAllPolygons = false; |
797 | |
|
798 | 0 | if (poMS->addGeometry(poMS2->getGeometryRef(i)) != OGRERR_NONE) |
799 | 0 | { |
800 | 0 | return false; |
801 | 0 | } |
802 | 0 | } |
803 | 0 | } |
804 | 0 | else |
805 | 0 | { |
806 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Got %s geometry as %s.", |
807 | 0 | poGeom->getGeometryName(), pszMemberElement); |
808 | 0 | return false; |
809 | 0 | } |
810 | 0 | return true; |
811 | 0 | } |
812 | | |
813 | | /************************************************************************/ |
814 | | /* GetUOMInMetre() */ |
815 | | /************************************************************************/ |
816 | | |
817 | | static double GetUOMInMetre(const char *pszUnits, const char *pszAttribute, |
818 | | const char *pszId) |
819 | 0 | { |
820 | 0 | if (!pszUnits || EQUAL(pszUnits, "m")) |
821 | 0 | return 1.0; |
822 | | |
823 | 0 | if (EQUAL(pszUnits, "km")) |
824 | 0 | return 1000.0; |
825 | | |
826 | 0 | if (EQUAL(pszUnits, "nm") || EQUAL(pszUnits, "[nmi_i]")) |
827 | 0 | return CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV); |
828 | | |
829 | 0 | if (EQUAL(pszUnits, "mi")) |
830 | 0 | return CPLAtof(SRS_UL_INTL_STAT_MILE_CONV); |
831 | | |
832 | 0 | if (EQUAL(pszUnits, "ft")) |
833 | 0 | return CPLAtof(SRS_UL_INTL_FOOT_CONV); |
834 | | |
835 | 0 | if (pszId) |
836 | 0 | { |
837 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
838 | 0 | "GML geometry id='%s': Unhandled distance unit '%s' in " |
839 | 0 | "attribute '%s'", |
840 | 0 | pszId, pszUnits, pszAttribute); |
841 | 0 | } |
842 | 0 | else |
843 | 0 | { |
844 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
845 | 0 | "Unhandled distance unit '%s' in attribute '%s'", pszUnits, |
846 | 0 | pszAttribute); |
847 | 0 | } |
848 | 0 | return -1; |
849 | 0 | } |
850 | | |
851 | | /************************************************************************/ |
852 | | /* GetSemiMajor() */ |
853 | | /************************************************************************/ |
854 | | |
855 | | static double GetSemiMajor(const OGRSpatialReference *poSRS) |
856 | 0 | { |
857 | 0 | double dfSemiMajor = poSRS->GetSemiMajor(); |
858 | | // Standardize on OGR_GREATCIRCLE_DEFAULT_RADIUS for Earth ellipsoids. |
859 | 0 | if (std::fabs(dfSemiMajor - OGR_GREATCIRCLE_DEFAULT_RADIUS) < |
860 | 0 | 0.05 * OGR_GREATCIRCLE_DEFAULT_RADIUS) |
861 | 0 | dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS; |
862 | 0 | return dfSemiMajor; |
863 | 0 | } |
864 | | |
865 | | /************************************************************************/ |
866 | | /* GML2OGRGeometry_XMLNode() */ |
867 | | /* */ |
868 | | /* Translates the passed XMLnode and its children into an */ |
869 | | /* OGRGeometry. This is used recursively for geometry */ |
870 | | /* collections. */ |
871 | | /************************************************************************/ |
872 | | |
873 | | static std::unique_ptr<OGRGeometry> GML2OGRGeometry_XMLNode_Internal( |
874 | | const CPLXMLNode *psNode, const char *pszId, |
875 | | int nPseudoBoolGetSecondaryGeometryOption, int nRecLevel, int nSRSDimension, |
876 | | const char *pszSRSName, bool bIgnoreGSG = false, bool bOrientation = true, |
877 | | bool bFaceHoleNegative = false); |
878 | | |
879 | | OGRGeometry *GML2OGRGeometry_XMLNode(const CPLXMLNode *psNode, |
880 | | int nPseudoBoolGetSecondaryGeometryOption, |
881 | | int nRecLevel, int nSRSDimension, |
882 | | bool bIgnoreGSG, bool bOrientation, |
883 | | bool bFaceHoleNegative, const char *pszId) |
884 | | |
885 | 0 | { |
886 | 0 | return GML2OGRGeometry_XMLNode_Internal( |
887 | 0 | psNode, pszId, nPseudoBoolGetSecondaryGeometryOption, nRecLevel, |
888 | 0 | nSRSDimension, nullptr, bIgnoreGSG, bOrientation, |
889 | 0 | bFaceHoleNegative) |
890 | 0 | .release(); |
891 | 0 | } |
892 | | |
893 | | static void ReportError(const char *pszId, CPLErr eErr, const char *fmt, ...) |
894 | | CPL_PRINT_FUNC_FORMAT(3, 4); |
895 | | |
896 | | static void ReportError(const char *pszId, CPLErr eErr, const char *fmt, ...) |
897 | 0 | { |
898 | 0 | va_list ap; |
899 | 0 | va_start(ap, fmt); |
900 | 0 | if (pszId) |
901 | 0 | { |
902 | 0 | std::string osMsg("GML geometry id='"); |
903 | 0 | osMsg += pszId; |
904 | 0 | osMsg += "': "; |
905 | 0 | osMsg += CPLString().vPrintf(fmt, ap); |
906 | 0 | CPLError(eErr, CPLE_AppDefined, "%s", osMsg.c_str()); |
907 | 0 | } |
908 | 0 | else |
909 | 0 | { |
910 | 0 | CPLErrorV(eErr, CPLE_AppDefined, fmt, ap); |
911 | 0 | } |
912 | 0 | va_end(ap); |
913 | 0 | } |
914 | | |
915 | | static std::unique_ptr<OGRGeometry> |
916 | | GML2OGRGeometry_XMLNode_Internal(const CPLXMLNode *psNode, const char *pszId, |
917 | | int nPseudoBoolGetSecondaryGeometryOption, |
918 | | int nRecLevel, int nSRSDimension, |
919 | | const char *pszSRSName, bool bIgnoreGSG, |
920 | | bool bOrientation, bool bFaceHoleNegative) |
921 | 0 | { |
922 | | // constexpr bool bCastToLinearTypeIfPossible = true; // Hard-coded for |
923 | | // now. |
924 | | |
925 | | // We need this nRecLevel == 0 check, otherwise this could result in |
926 | | // multiple revist of the same node, and exponential complexity. |
927 | 0 | if (nRecLevel == 0 && psNode != nullptr && |
928 | 0 | strcmp(psNode->pszValue, "?xml") == 0) |
929 | 0 | psNode = psNode->psNext; |
930 | 0 | while (psNode != nullptr && psNode->eType == CXT_Comment) |
931 | 0 | psNode = psNode->psNext; |
932 | 0 | if (psNode == nullptr) |
933 | 0 | return nullptr; |
934 | | |
935 | 0 | const char *pszSRSDimension = |
936 | 0 | CPLGetXMLValue(psNode, "srsDimension", nullptr); |
937 | 0 | if (pszSRSDimension != nullptr) |
938 | 0 | nSRSDimension = atoi(pszSRSDimension); |
939 | |
|
940 | 0 | if (pszSRSName == nullptr) |
941 | 0 | pszSRSName = CPLGetXMLValue(psNode, "srsName", nullptr); |
942 | |
|
943 | 0 | if (!pszId && nRecLevel == 0) |
944 | 0 | { |
945 | 0 | pszId = CPLGetXMLValue(psNode, "gml:id", nullptr); |
946 | 0 | } |
947 | |
|
948 | 0 | const char *pszBaseGeometry = BareGMLElement(psNode->pszValue); |
949 | 0 | if (nPseudoBoolGetSecondaryGeometryOption < 0) |
950 | 0 | nPseudoBoolGetSecondaryGeometryOption = |
951 | 0 | CPLTestBool(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO")); |
952 | 0 | bool bGetSecondaryGeometry = |
953 | 0 | bIgnoreGSG ? false : CPL_TO_BOOL(nPseudoBoolGetSecondaryGeometryOption); |
954 | |
|
955 | 0 | #define ReportFailure(...) ReportError(pszId, CE_Failure, __VA_ARGS__) |
956 | |
|
957 | 0 | #define ReportWarning(...) ReportError(pszId, CE_Warning, __VA_ARGS__) |
958 | | |
959 | | // Arbitrary value, but certainly large enough for reasonable usages. |
960 | 0 | if (nRecLevel == 32) |
961 | 0 | { |
962 | 0 | ReportFailure( |
963 | 0 | "Too many recursion levels (%d) while parsing GML geometry.", |
964 | 0 | nRecLevel); |
965 | 0 | return nullptr; |
966 | 0 | } |
967 | | |
968 | 0 | if (bGetSecondaryGeometry) |
969 | 0 | if (!(EQUAL(pszBaseGeometry, "directedEdge") || |
970 | 0 | EQUAL(pszBaseGeometry, "TopoCurve"))) |
971 | 0 | return nullptr; |
972 | | |
973 | | /* -------------------------------------------------------------------- */ |
974 | | /* Polygon / PolygonPatch / Rectangle */ |
975 | | /* -------------------------------------------------------------------- */ |
976 | 0 | if (EQUAL(pszBaseGeometry, "Polygon") || |
977 | 0 | EQUAL(pszBaseGeometry, "PolygonPatch") || |
978 | 0 | EQUAL(pszBaseGeometry, "Rectangle")) |
979 | 0 | { |
980 | | // Find outer ring. |
981 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "outerBoundaryIs"); |
982 | 0 | if (psChild == nullptr) |
983 | 0 | psChild = FindBareXMLChild(psNode, "exterior"); |
984 | |
|
985 | 0 | psChild = GetChildElement(psChild); |
986 | 0 | if (psChild == nullptr) |
987 | 0 | { |
988 | | // <gml:Polygon/> is invalid GML2, but valid GML3, so be tolerant. |
989 | 0 | return std::make_unique<OGRPolygon>(); |
990 | 0 | } |
991 | | |
992 | | // Translate outer ring and add to polygon. |
993 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
994 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
995 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
996 | 0 | if (poGeom == nullptr) |
997 | 0 | { |
998 | 0 | ReportFailure("Invalid exterior ring"); |
999 | 0 | return nullptr; |
1000 | 0 | } |
1001 | | |
1002 | 0 | if (!OGR_GT_IsCurve(poGeom->getGeometryType())) |
1003 | 0 | { |
1004 | 0 | ReportFailure("%s: Got %s geometry as outerBoundaryIs.", |
1005 | 0 | pszBaseGeometry, poGeom->getGeometryName()); |
1006 | 0 | return nullptr; |
1007 | 0 | } |
1008 | | |
1009 | 0 | if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString && |
1010 | 0 | !EQUAL(poGeom->getGeometryName(), "LINEARRING")) |
1011 | 0 | { |
1012 | 0 | OGRCurve *poCurve = poGeom.release()->toCurve(); |
1013 | 0 | auto poLinearRing = OGRCurve::CastToLinearRing(poCurve); |
1014 | 0 | if (!poLinearRing) |
1015 | 0 | return nullptr; |
1016 | 0 | poGeom.reset(poLinearRing); |
1017 | 0 | } |
1018 | | |
1019 | 0 | std::unique_ptr<OGRCurvePolygon> poCP; |
1020 | 0 | bool bIsPolygon = false; |
1021 | 0 | assert(poGeom); // to please cppcheck |
1022 | 0 | if (EQUAL(poGeom->getGeometryName(), "LINEARRING")) |
1023 | 0 | { |
1024 | 0 | poCP = std::make_unique<OGRPolygon>(); |
1025 | 0 | bIsPolygon = true; |
1026 | 0 | } |
1027 | 0 | else |
1028 | 0 | { |
1029 | 0 | poCP = std::make_unique<OGRCurvePolygon>(); |
1030 | 0 | bIsPolygon = false; |
1031 | 0 | } |
1032 | |
|
1033 | 0 | { |
1034 | 0 | auto poCurve = |
1035 | 0 | std::unique_ptr<OGRCurve>(poGeom.release()->toCurve()); |
1036 | 0 | if (poCP->addRing(std::move(poCurve)) != OGRERR_NONE) |
1037 | 0 | { |
1038 | 0 | return nullptr; |
1039 | 0 | } |
1040 | 0 | } |
1041 | | |
1042 | | // Find all inner rings |
1043 | 0 | for (psChild = psNode->psChild; psChild != nullptr; |
1044 | 0 | psChild = psChild->psNext) |
1045 | 0 | { |
1046 | 0 | if (psChild->eType == CXT_Element && |
1047 | 0 | (EQUAL(BareGMLElement(psChild->pszValue), "innerBoundaryIs") || |
1048 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "interior"))) |
1049 | 0 | { |
1050 | 0 | const CPLXMLNode *psInteriorChild = GetChildElement(psChild); |
1051 | 0 | std::unique_ptr<OGRGeometry> poGeomInterior; |
1052 | 0 | if (psInteriorChild != nullptr) |
1053 | 0 | poGeomInterior = GML2OGRGeometry_XMLNode_Internal( |
1054 | 0 | psInteriorChild, pszId, |
1055 | 0 | nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1, |
1056 | 0 | nSRSDimension, pszSRSName); |
1057 | 0 | if (poGeomInterior == nullptr) |
1058 | 0 | { |
1059 | 0 | ReportFailure("Invalid interior ring"); |
1060 | 0 | return nullptr; |
1061 | 0 | } |
1062 | | |
1063 | 0 | if (!OGR_GT_IsCurve(poGeomInterior->getGeometryType())) |
1064 | 0 | { |
1065 | 0 | ReportFailure("%s: Got %s geometry as innerBoundaryIs.", |
1066 | 0 | pszBaseGeometry, |
1067 | 0 | poGeomInterior->getGeometryName()); |
1068 | 0 | return nullptr; |
1069 | 0 | } |
1070 | | |
1071 | 0 | if (bIsPolygon) |
1072 | 0 | { |
1073 | 0 | if (!EQUAL(poGeomInterior->getGeometryName(), "LINEARRING")) |
1074 | 0 | { |
1075 | 0 | if (wkbFlatten(poGeomInterior->getGeometryType()) == |
1076 | 0 | wkbLineString) |
1077 | 0 | { |
1078 | 0 | OGRLineString *poLS = |
1079 | 0 | poGeomInterior.release()->toLineString(); |
1080 | 0 | auto poLinearRing = |
1081 | 0 | OGRCurve::CastToLinearRing(poLS); |
1082 | 0 | if (!poLinearRing) |
1083 | 0 | return nullptr; |
1084 | 0 | poGeomInterior.reset(poLinearRing); |
1085 | 0 | } |
1086 | 0 | else |
1087 | 0 | { |
1088 | | // Might fail if some rings are not closed. |
1089 | | // We used to be tolerant about that with Polygon. |
1090 | | // but we have become stricter with CurvePolygon. |
1091 | 0 | auto poCPNew = std::unique_ptr<OGRCurvePolygon>( |
1092 | 0 | OGRSurface::CastToCurvePolygon(poCP.release())); |
1093 | 0 | if (!poCPNew) |
1094 | 0 | { |
1095 | 0 | return nullptr; |
1096 | 0 | } |
1097 | 0 | poCP = std::move(poCPNew); |
1098 | 0 | bIsPolygon = false; |
1099 | 0 | } |
1100 | 0 | } |
1101 | 0 | } |
1102 | 0 | else |
1103 | 0 | { |
1104 | 0 | if (EQUAL(poGeomInterior->getGeometryName(), "LINEARRING")) |
1105 | 0 | { |
1106 | 0 | OGRCurve *poCurve = poGeomInterior.release()->toCurve(); |
1107 | 0 | poGeomInterior.reset( |
1108 | 0 | OGRCurve::CastToLineString(poCurve)); |
1109 | 0 | } |
1110 | 0 | } |
1111 | 0 | auto poCurve = std::unique_ptr<OGRCurve>( |
1112 | 0 | poGeomInterior.release()->toCurve()); |
1113 | 0 | if (poCP->addRing(std::move(poCurve)) != OGRERR_NONE) |
1114 | 0 | { |
1115 | 0 | return nullptr; |
1116 | 0 | } |
1117 | 0 | } |
1118 | 0 | } |
1119 | | |
1120 | 0 | return poCP; |
1121 | 0 | } |
1122 | | |
1123 | | /* -------------------------------------------------------------------- */ |
1124 | | /* Triangle */ |
1125 | | /* -------------------------------------------------------------------- */ |
1126 | | |
1127 | 0 | if (EQUAL(pszBaseGeometry, "Triangle")) |
1128 | 0 | { |
1129 | | // Find outer ring. |
1130 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "exterior"); |
1131 | 0 | if (!psChild) |
1132 | 0 | return nullptr; |
1133 | | |
1134 | 0 | psChild = GetChildElement(psChild); |
1135 | 0 | if (psChild == nullptr) |
1136 | 0 | { |
1137 | 0 | ReportFailure("Empty Triangle"); |
1138 | 0 | return std::make_unique<OGRTriangle>(); |
1139 | 0 | } |
1140 | | |
1141 | | // Translate outer ring and add to Triangle. |
1142 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
1143 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
1144 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
1145 | 0 | if (poGeom == nullptr) |
1146 | 0 | { |
1147 | 0 | ReportFailure("Invalid exterior ring"); |
1148 | 0 | return nullptr; |
1149 | 0 | } |
1150 | | |
1151 | 0 | if (!OGR_GT_IsCurve(poGeom->getGeometryType())) |
1152 | 0 | { |
1153 | 0 | ReportFailure("%s: Got %s geometry as outerBoundaryIs.", |
1154 | 0 | pszBaseGeometry, poGeom->getGeometryName()); |
1155 | 0 | return nullptr; |
1156 | 0 | } |
1157 | | |
1158 | 0 | if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString && |
1159 | 0 | !EQUAL(poGeom->getGeometryName(), "LINEARRING")) |
1160 | 0 | { |
1161 | 0 | poGeom.reset( |
1162 | 0 | OGRCurve::CastToLinearRing(poGeom.release()->toCurve())); |
1163 | 0 | } |
1164 | |
|
1165 | 0 | if (poGeom == nullptr || |
1166 | 0 | !EQUAL(poGeom->getGeometryName(), "LINEARRING")) |
1167 | 0 | { |
1168 | 0 | return nullptr; |
1169 | 0 | } |
1170 | | |
1171 | 0 | auto poTriangle = std::make_unique<OGRTriangle>(); |
1172 | 0 | auto poCurve = std::unique_ptr<OGRCurve>(poGeom.release()->toCurve()); |
1173 | 0 | if (poTriangle->addRing(std::move(poCurve)) != OGRERR_NONE) |
1174 | 0 | { |
1175 | 0 | return nullptr; |
1176 | 0 | } |
1177 | | |
1178 | 0 | return poTriangle; |
1179 | 0 | } |
1180 | | |
1181 | | /* -------------------------------------------------------------------- */ |
1182 | | /* LinearRing */ |
1183 | | /* -------------------------------------------------------------------- */ |
1184 | 0 | if (EQUAL(pszBaseGeometry, "LinearRing")) |
1185 | 0 | { |
1186 | 0 | auto poLinearRing = std::make_unique<OGRLinearRing>(); |
1187 | |
|
1188 | 0 | if (!ParseGMLCoordinates(psNode, poLinearRing.get(), nSRSDimension)) |
1189 | 0 | { |
1190 | 0 | return nullptr; |
1191 | 0 | } |
1192 | | |
1193 | 0 | return poLinearRing; |
1194 | 0 | } |
1195 | | |
1196 | 0 | const auto storeArcByCenterPointParameters = |
1197 | 0 | [pszId](const CPLXMLNode *psChild, const char *l_pszSRSName, |
1198 | 0 | bool &bIsApproximateArc, |
1199 | 0 | double &dfLastCurveApproximateArcRadius, |
1200 | 0 | bool &bLastCurveWasApproximateArcInvertedAxisOrder, |
1201 | 0 | double &dfSemiMajor) |
1202 | 0 | { |
1203 | 0 | const CPLXMLNode *psRadius = FindBareXMLChild(psChild, "radius"); |
1204 | 0 | if (psRadius && psRadius->eType == CXT_Element) |
1205 | 0 | { |
1206 | 0 | const char *pszUnits = CPLGetXMLValue(psRadius, "uom", nullptr); |
1207 | 0 | const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId); |
1208 | 0 | const double dfRadiusRaw = |
1209 | 0 | CPLAtof(CPLGetXMLValue(psRadius, nullptr, "0")); |
1210 | 0 | const double dfRadius = |
1211 | 0 | dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw; |
1212 | 0 | bool bSRSUnitIsDegree = false; |
1213 | 0 | bool bInvertedAxisOrder = false; |
1214 | 0 | if (l_pszSRSName != nullptr) |
1215 | 0 | { |
1216 | 0 | OGRSpatialReference oSRS; |
1217 | 0 | if (oSRS.SetFromUserInput(l_pszSRSName) == OGRERR_NONE) |
1218 | 0 | { |
1219 | 0 | if (oSRS.IsGeographic()) |
1220 | 0 | { |
1221 | 0 | bInvertedAxisOrder = |
1222 | 0 | CPL_TO_BOOL(oSRS.EPSGTreatsAsLatLong()); |
1223 | 0 | dfSemiMajor = GetSemiMajor(&oSRS); |
1224 | 0 | bSRSUnitIsDegree = |
1225 | 0 | fabs(oSRS.GetAngularUnits(nullptr) - |
1226 | 0 | CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8; |
1227 | 0 | } |
1228 | 0 | } |
1229 | 0 | } |
1230 | 0 | if (bSRSUnitIsDegree && dfUOMConv > 0) |
1231 | 0 | { |
1232 | 0 | bIsApproximateArc = true; |
1233 | 0 | dfLastCurveApproximateArcRadius = dfRadius; |
1234 | 0 | bLastCurveWasApproximateArcInvertedAxisOrder = |
1235 | 0 | bInvertedAxisOrder; |
1236 | 0 | } |
1237 | 0 | } |
1238 | 0 | }; |
1239 | |
|
1240 | 0 | const auto connectArcByCenterPointToOtherSegments = |
1241 | 0 | [](OGRGeometry *poGeom, OGRCompoundCurve *poCC, |
1242 | 0 | const bool bIsApproximateArc, const bool bLastCurveWasApproximateArc, |
1243 | 0 | const double dfLastCurveApproximateArcRadius, |
1244 | 0 | const bool bLastCurveWasApproximateArcInvertedAxisOrder, |
1245 | 0 | const double dfSemiMajor) |
1246 | 0 | { |
1247 | 0 | if (bIsApproximateArc) |
1248 | 0 | { |
1249 | 0 | if (poGeom->getGeometryType() == wkbLineString) |
1250 | 0 | { |
1251 | 0 | OGRCurve *poPreviousCurve = |
1252 | 0 | poCC->getCurve(poCC->getNumCurves() - 1); |
1253 | 0 | OGRLineString *poLS = poGeom->toLineString(); |
1254 | 0 | if (poPreviousCurve->getNumPoints() >= 2 && |
1255 | 0 | poLS->getNumPoints() >= 2) |
1256 | 0 | { |
1257 | 0 | OGRPoint p; |
1258 | 0 | OGRPoint p2; |
1259 | 0 | poPreviousCurve->EndPoint(&p); |
1260 | 0 | poLS->StartPoint(&p2); |
1261 | 0 | double dfDistance = 0.0; |
1262 | 0 | if (bLastCurveWasApproximateArcInvertedAxisOrder) |
1263 | 0 | dfDistance = OGR_GreatCircle_Distance( |
1264 | 0 | p.getX(), p.getY(), p2.getX(), p2.getY(), |
1265 | 0 | dfSemiMajor); |
1266 | 0 | else |
1267 | 0 | dfDistance = OGR_GreatCircle_Distance( |
1268 | 0 | p.getY(), p.getX(), p2.getY(), p2.getX(), |
1269 | 0 | dfSemiMajor); |
1270 | | // CPLDebug("OGR", "%f %f", |
1271 | | // dfDistance, |
1272 | | // dfLastCurveApproximateArcRadius |
1273 | | // / 10.0 ); |
1274 | 0 | if (dfDistance < dfLastCurveApproximateArcRadius / 5.0) |
1275 | 0 | { |
1276 | 0 | CPLDebug("OGR", "Moving approximate start of " |
1277 | 0 | "ArcByCenterPoint to end of " |
1278 | 0 | "previous curve"); |
1279 | 0 | poLS->setPoint(0, &p); |
1280 | 0 | } |
1281 | 0 | } |
1282 | 0 | } |
1283 | 0 | } |
1284 | 0 | else if (bLastCurveWasApproximateArc) |
1285 | 0 | { |
1286 | 0 | OGRCurve *poPreviousCurve = |
1287 | 0 | poCC->getCurve(poCC->getNumCurves() - 1); |
1288 | 0 | if (poPreviousCurve->getGeometryType() == wkbLineString) |
1289 | 0 | { |
1290 | 0 | OGRLineString *poLS = poPreviousCurve->toLineString(); |
1291 | 0 | OGRCurve *poAsCurve = poGeom->toCurve(); |
1292 | |
|
1293 | 0 | if (poLS->getNumPoints() >= 2 && poAsCurve->getNumPoints() >= 2) |
1294 | 0 | { |
1295 | 0 | OGRPoint p; |
1296 | 0 | OGRPoint p2; |
1297 | 0 | poAsCurve->StartPoint(&p); |
1298 | 0 | poLS->EndPoint(&p2); |
1299 | 0 | double dfDistance = 0.0; |
1300 | 0 | if (bLastCurveWasApproximateArcInvertedAxisOrder) |
1301 | 0 | dfDistance = OGR_GreatCircle_Distance( |
1302 | 0 | p.getX(), p.getY(), p2.getX(), p2.getY(), |
1303 | 0 | dfSemiMajor); |
1304 | 0 | else |
1305 | 0 | dfDistance = OGR_GreatCircle_Distance( |
1306 | 0 | p.getY(), p.getX(), p2.getY(), p2.getX(), |
1307 | 0 | dfSemiMajor); |
1308 | | // CPLDebug( |
1309 | | // "OGR", "%f %f", |
1310 | | // dfDistance, |
1311 | | // dfLastCurveApproximateArcRadius / 10.0 ); |
1312 | | |
1313 | | // "A-311 WHEELER AFB OAHU, HI.xml" needs more |
1314 | | // than 10%. |
1315 | 0 | if (dfDistance < dfLastCurveApproximateArcRadius / 5.0) |
1316 | 0 | { |
1317 | 0 | CPLDebug("OGR", "Moving approximate end of last " |
1318 | 0 | "ArcByCenterPoint to start of the " |
1319 | 0 | "current curve"); |
1320 | 0 | poLS->setPoint(poLS->getNumPoints() - 1, &p); |
1321 | 0 | } |
1322 | 0 | } |
1323 | 0 | } |
1324 | 0 | } |
1325 | 0 | }; |
1326 | | |
1327 | | /* -------------------------------------------------------------------- */ |
1328 | | /* Ring GML3 */ |
1329 | | /* -------------------------------------------------------------------- */ |
1330 | 0 | if (EQUAL(pszBaseGeometry, "Ring")) |
1331 | 0 | { |
1332 | 0 | std::unique_ptr<OGRCurve> poRing; |
1333 | 0 | std::unique_ptr<OGRCompoundCurve> poCC; |
1334 | 0 | bool bChildrenAreAllLineString = true; |
1335 | |
|
1336 | 0 | bool bLastCurveWasApproximateArc = false; |
1337 | 0 | bool bLastCurveWasApproximateArcInvertedAxisOrder = false; |
1338 | 0 | double dfLastCurveApproximateArcRadius = 0.0; |
1339 | |
|
1340 | 0 | bool bIsFirstChild = true; |
1341 | 0 | bool bFirstChildIsApproximateArc = false; |
1342 | 0 | double dfFirstChildApproximateArcRadius = 0.0; |
1343 | 0 | bool bFirstChildWasApproximateArcInvertedAxisOrder = false; |
1344 | |
|
1345 | 0 | double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS; |
1346 | |
|
1347 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
1348 | 0 | psChild = psChild->psNext) |
1349 | 0 | { |
1350 | 0 | if (psChild->eType == CXT_Element && |
1351 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "curveMember")) |
1352 | 0 | { |
1353 | 0 | const CPLXMLNode *psCurveChild = GetChildElement(psChild); |
1354 | 0 | std::unique_ptr<OGRGeometry> poGeom; |
1355 | 0 | if (psCurveChild != nullptr) |
1356 | 0 | { |
1357 | 0 | poGeom = GML2OGRGeometry_XMLNode_Internal( |
1358 | 0 | psCurveChild, pszId, |
1359 | 0 | nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1, |
1360 | 0 | nSRSDimension, pszSRSName); |
1361 | 0 | } |
1362 | 0 | else |
1363 | 0 | { |
1364 | 0 | if (psChild->psChild && |
1365 | 0 | psChild->psChild->eType == CXT_Attribute && |
1366 | 0 | psChild->psChild->psNext == nullptr && |
1367 | 0 | strcmp(psChild->psChild->pszValue, "xlink:href") == 0) |
1368 | 0 | { |
1369 | 0 | ReportWarning("Cannot resolve xlink:href='%s'. " |
1370 | 0 | "Try setting GML_SKIP_RESOLVE_ELEMS=NONE", |
1371 | 0 | psChild->psChild->psChild->pszValue); |
1372 | 0 | } |
1373 | 0 | return nullptr; |
1374 | 0 | } |
1375 | | |
1376 | | // Try to join multiline string to one linestring. |
1377 | 0 | if (poGeom && |
1378 | 0 | wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString) |
1379 | 0 | { |
1380 | 0 | poGeom.reset(OGRGeometryFactory::forceToLineString( |
1381 | 0 | poGeom.release(), false)); |
1382 | 0 | } |
1383 | |
|
1384 | 0 | if (poGeom == nullptr || |
1385 | 0 | !OGR_GT_IsCurve(poGeom->getGeometryType())) |
1386 | 0 | { |
1387 | 0 | return nullptr; |
1388 | 0 | } |
1389 | | |
1390 | 0 | if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString) |
1391 | 0 | bChildrenAreAllLineString = false; |
1392 | | |
1393 | | // Ad-hoc logic to handle nicely connecting ArcByCenterPoint |
1394 | | // with consecutive curves, as found in some AIXM files. |
1395 | 0 | bool bIsApproximateArc = false; |
1396 | 0 | const CPLXMLNode *psChild2, *psChild3; |
1397 | 0 | if (strcmp(BareGMLElement(psCurveChild->pszValue), "Curve") == |
1398 | 0 | 0 && |
1399 | 0 | (psChild2 = GetChildElement(psCurveChild)) != nullptr && |
1400 | 0 | strcmp(BareGMLElement(psChild2->pszValue), "segments") == |
1401 | 0 | 0 && |
1402 | 0 | (psChild3 = GetChildElement(psChild2)) != nullptr && |
1403 | 0 | strcmp(BareGMLElement(psChild3->pszValue), |
1404 | 0 | "ArcByCenterPoint") == 0) |
1405 | 0 | { |
1406 | 0 | storeArcByCenterPointParameters( |
1407 | 0 | psChild3, pszSRSName, bIsApproximateArc, |
1408 | 0 | dfLastCurveApproximateArcRadius, |
1409 | 0 | bLastCurveWasApproximateArcInvertedAxisOrder, |
1410 | 0 | dfSemiMajor); |
1411 | 0 | if (bIsFirstChild && bIsApproximateArc) |
1412 | 0 | { |
1413 | 0 | bFirstChildIsApproximateArc = true; |
1414 | 0 | dfFirstChildApproximateArcRadius = |
1415 | 0 | dfLastCurveApproximateArcRadius; |
1416 | 0 | bFirstChildWasApproximateArcInvertedAxisOrder = |
1417 | 0 | bLastCurveWasApproximateArcInvertedAxisOrder; |
1418 | 0 | } |
1419 | 0 | else if (psChild3->psNext) |
1420 | 0 | { |
1421 | 0 | bIsApproximateArc = false; |
1422 | 0 | } |
1423 | 0 | } |
1424 | 0 | bIsFirstChild = false; |
1425 | |
|
1426 | 0 | if (poCC == nullptr && poRing == nullptr) |
1427 | 0 | { |
1428 | 0 | poRing.reset(poGeom.release()->toCurve()); |
1429 | 0 | } |
1430 | 0 | else |
1431 | 0 | { |
1432 | 0 | if (poCC == nullptr) |
1433 | 0 | { |
1434 | 0 | poCC = std::make_unique<OGRCompoundCurve>(); |
1435 | 0 | bool bIgnored = false; |
1436 | 0 | if (!GML2OGRGeometry_AddToCompositeCurve( |
1437 | 0 | poCC.get(), std::move(poRing), bIgnored)) |
1438 | 0 | { |
1439 | 0 | return nullptr; |
1440 | 0 | } |
1441 | 0 | poRing.reset(); |
1442 | 0 | } |
1443 | | |
1444 | 0 | connectArcByCenterPointToOtherSegments( |
1445 | 0 | poGeom.get(), poCC.get(), bIsApproximateArc, |
1446 | 0 | bLastCurveWasApproximateArc, |
1447 | 0 | dfLastCurveApproximateArcRadius, |
1448 | 0 | bLastCurveWasApproximateArcInvertedAxisOrder, |
1449 | 0 | dfSemiMajor); |
1450 | |
|
1451 | 0 | auto poCurve = |
1452 | 0 | std::unique_ptr<OGRCurve>(poGeom.release()->toCurve()); |
1453 | |
|
1454 | 0 | bool bIgnored = false; |
1455 | 0 | if (!GML2OGRGeometry_AddToCompositeCurve( |
1456 | 0 | poCC.get(), std::move(poCurve), bIgnored)) |
1457 | 0 | { |
1458 | 0 | return nullptr; |
1459 | 0 | } |
1460 | 0 | } |
1461 | | |
1462 | 0 | bLastCurveWasApproximateArc = bIsApproximateArc; |
1463 | 0 | } |
1464 | 0 | } |
1465 | | |
1466 | | /* Detect if the last object in the following hierarchy is a |
1467 | | ArcByCenterPoint <gml:Ring> <gml:curveMember> (may be repeated) |
1468 | | <gml:Curve> |
1469 | | <gml:segments> |
1470 | | .... |
1471 | | <gml:ArcByCenterPoint ... /> |
1472 | | </gml:segments> |
1473 | | </gml:Curve> |
1474 | | </gml:curveMember> |
1475 | | </gml:Ring> |
1476 | | */ |
1477 | 0 | bool bLastChildIsApproximateArc = false; |
1478 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
1479 | 0 | psChild = psChild->psNext) |
1480 | 0 | { |
1481 | 0 | if (psChild->eType == CXT_Element && |
1482 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "curveMember")) |
1483 | 0 | { |
1484 | 0 | const CPLXMLNode *psCurveMemberChild = GetChildElement(psChild); |
1485 | 0 | if (psCurveMemberChild && |
1486 | 0 | psCurveMemberChild->eType == CXT_Element && |
1487 | 0 | EQUAL(BareGMLElement(psCurveMemberChild->pszValue), |
1488 | 0 | "Curve")) |
1489 | 0 | { |
1490 | 0 | const CPLXMLNode *psCurveChild = |
1491 | 0 | GetChildElement(psCurveMemberChild); |
1492 | 0 | if (psCurveChild && psCurveChild->eType == CXT_Element && |
1493 | 0 | EQUAL(BareGMLElement(psCurveChild->pszValue), |
1494 | 0 | "segments")) |
1495 | 0 | { |
1496 | 0 | for (const CPLXMLNode *psChild2 = psCurveChild->psChild; |
1497 | 0 | psChild2 != nullptr; psChild2 = psChild2->psNext) |
1498 | 0 | { |
1499 | 0 | if (psChild2->eType == CXT_Element && |
1500 | 0 | EQUAL(BareGMLElement(psChild2->pszValue), |
1501 | 0 | "ArcByCenterPoint")) |
1502 | 0 | { |
1503 | 0 | storeArcByCenterPointParameters( |
1504 | 0 | psChild2, pszSRSName, |
1505 | 0 | bLastChildIsApproximateArc, |
1506 | 0 | dfLastCurveApproximateArcRadius, |
1507 | 0 | bLastCurveWasApproximateArcInvertedAxisOrder, |
1508 | 0 | dfSemiMajor); |
1509 | 0 | } |
1510 | 0 | else |
1511 | 0 | { |
1512 | 0 | bLastChildIsApproximateArc = false; |
1513 | 0 | } |
1514 | 0 | } |
1515 | 0 | } |
1516 | 0 | else |
1517 | 0 | { |
1518 | 0 | bLastChildIsApproximateArc = false; |
1519 | 0 | } |
1520 | 0 | } |
1521 | 0 | else |
1522 | 0 | { |
1523 | 0 | bLastChildIsApproximateArc = false; |
1524 | 0 | } |
1525 | 0 | } |
1526 | 0 | else |
1527 | 0 | { |
1528 | 0 | bLastChildIsApproximateArc = false; |
1529 | 0 | } |
1530 | 0 | } |
1531 | |
|
1532 | 0 | if (poRing) |
1533 | 0 | { |
1534 | 0 | if (poRing->getNumPoints() >= 2 && bFirstChildIsApproximateArc && |
1535 | 0 | !poRing->get_IsClosed() && |
1536 | 0 | wkbFlatten(poRing->getGeometryType()) == wkbLineString) |
1537 | 0 | { |
1538 | 0 | OGRLineString *poLS = poRing->toLineString(); |
1539 | |
|
1540 | 0 | OGRPoint p; |
1541 | 0 | OGRPoint p2; |
1542 | 0 | poLS->StartPoint(&p); |
1543 | 0 | poLS->EndPoint(&p2); |
1544 | 0 | double dfDistance = 0.0; |
1545 | 0 | if (bFirstChildWasApproximateArcInvertedAxisOrder) |
1546 | 0 | dfDistance = OGR_GreatCircle_Distance( |
1547 | 0 | p.getX(), p.getY(), p2.getX(), p2.getY(), dfSemiMajor); |
1548 | 0 | else |
1549 | 0 | dfDistance = OGR_GreatCircle_Distance( |
1550 | 0 | p.getY(), p.getX(), p2.getY(), p2.getX(), dfSemiMajor); |
1551 | 0 | if (dfDistance < dfFirstChildApproximateArcRadius / 5.0) |
1552 | 0 | { |
1553 | 0 | CPLDebug("OGR", "Moving approximate start of " |
1554 | 0 | "ArcByCenterPoint to end of " |
1555 | 0 | "curve"); |
1556 | 0 | poLS->setPoint(0, &p2); |
1557 | 0 | } |
1558 | 0 | } |
1559 | 0 | else if (poRing->getNumPoints() >= 2 && |
1560 | 0 | bLastChildIsApproximateArc && !poRing->get_IsClosed() && |
1561 | 0 | wkbFlatten(poRing->getGeometryType()) == wkbLineString) |
1562 | 0 | { |
1563 | 0 | OGRLineString *poLS = poRing->toLineString(); |
1564 | |
|
1565 | 0 | OGRPoint p; |
1566 | 0 | OGRPoint p2; |
1567 | 0 | poLS->StartPoint(&p); |
1568 | 0 | poLS->EndPoint(&p2); |
1569 | 0 | double dfDistance = 0.0; |
1570 | 0 | if (bLastCurveWasApproximateArcInvertedAxisOrder) |
1571 | 0 | dfDistance = OGR_GreatCircle_Distance( |
1572 | 0 | p.getX(), p.getY(), p2.getX(), p2.getY(), dfSemiMajor); |
1573 | 0 | else |
1574 | 0 | dfDistance = OGR_GreatCircle_Distance( |
1575 | 0 | p.getY(), p.getX(), p2.getY(), p2.getX(), dfSemiMajor); |
1576 | 0 | if (dfDistance < dfLastCurveApproximateArcRadius / 5.0) |
1577 | 0 | { |
1578 | 0 | CPLDebug("OGR", "Moving approximate end of " |
1579 | 0 | "ArcByCenterPoint to start of " |
1580 | 0 | "curve"); |
1581 | 0 | poLS->setPoint(poLS->getNumPoints() - 1, &p); |
1582 | 0 | } |
1583 | 0 | } |
1584 | |
|
1585 | 0 | if (poRing->getNumPoints() < 2 || !poRing->get_IsClosed()) |
1586 | 0 | { |
1587 | 0 | ReportFailure("Non-closed ring"); |
1588 | 0 | return nullptr; |
1589 | 0 | } |
1590 | 0 | return poRing; |
1591 | 0 | } |
1592 | | |
1593 | 0 | if (poCC == nullptr) |
1594 | 0 | return nullptr; |
1595 | | |
1596 | 0 | else if (/* bCastToLinearTypeIfPossible &&*/ bChildrenAreAllLineString) |
1597 | 0 | { |
1598 | 0 | return std::unique_ptr<OGRLinearRing>( |
1599 | 0 | OGRCurve::CastToLinearRing(poCC.release())); |
1600 | 0 | } |
1601 | 0 | else |
1602 | 0 | { |
1603 | 0 | if (poCC->getNumPoints() < 2 || !poCC->get_IsClosed()) |
1604 | 0 | { |
1605 | 0 | ReportFailure("Non-closed ring"); |
1606 | 0 | return nullptr; |
1607 | 0 | } |
1608 | 0 | return poCC; |
1609 | 0 | } |
1610 | 0 | } |
1611 | | |
1612 | | /* -------------------------------------------------------------------- */ |
1613 | | /* LineString */ |
1614 | | /* -------------------------------------------------------------------- */ |
1615 | 0 | if (EQUAL(pszBaseGeometry, "LineString") || |
1616 | 0 | EQUAL(pszBaseGeometry, "LineStringSegment") || |
1617 | 0 | EQUAL(pszBaseGeometry, "Geodesic") || |
1618 | 0 | EQUAL(pszBaseGeometry, "GeodesicString")) |
1619 | 0 | { |
1620 | 0 | auto poLine = std::make_unique<OGRLineString>(); |
1621 | |
|
1622 | 0 | if (!ParseGMLCoordinates(psNode, poLine.get(), nSRSDimension)) |
1623 | 0 | { |
1624 | 0 | return nullptr; |
1625 | 0 | } |
1626 | | |
1627 | 0 | return poLine; |
1628 | 0 | } |
1629 | | |
1630 | | /* -------------------------------------------------------------------- */ |
1631 | | /* Arc */ |
1632 | | /* -------------------------------------------------------------------- */ |
1633 | 0 | if (EQUAL(pszBaseGeometry, "Arc")) |
1634 | 0 | { |
1635 | 0 | auto poCC = std::make_unique<OGRCircularString>(); |
1636 | |
|
1637 | 0 | if (!ParseGMLCoordinates(psNode, poCC.get(), nSRSDimension)) |
1638 | 0 | { |
1639 | 0 | return nullptr; |
1640 | 0 | } |
1641 | | |
1642 | | // Normally a gml:Arc has only 3 points of controls, but in the |
1643 | | // wild we sometimes find GML with 5 points, so accept any odd |
1644 | | // number >= 3 (ArcString should be used for > 3 points) |
1645 | 0 | if (poCC->getNumPoints() < 3 || (poCC->getNumPoints() % 2) != 1) |
1646 | 0 | { |
1647 | 0 | ReportFailure("Bad number of points in Arc"); |
1648 | 0 | return nullptr; |
1649 | 0 | } |
1650 | | |
1651 | 0 | return poCC; |
1652 | 0 | } |
1653 | | |
1654 | | /* -------------------------------------------------------------------- */ |
1655 | | /* ArcString */ |
1656 | | /* -------------------------------------------------------------------- */ |
1657 | 0 | if (EQUAL(pszBaseGeometry, "ArcString")) |
1658 | 0 | { |
1659 | 0 | auto poCC = std::make_unique<OGRCircularString>(); |
1660 | |
|
1661 | 0 | if (!ParseGMLCoordinates(psNode, poCC.get(), nSRSDimension)) |
1662 | 0 | { |
1663 | 0 | return nullptr; |
1664 | 0 | } |
1665 | | |
1666 | 0 | if (poCC->getNumPoints() < 3 || (poCC->getNumPoints() % 2) != 1) |
1667 | 0 | { |
1668 | 0 | ReportFailure("Bad number of points in ArcString"); |
1669 | 0 | return nullptr; |
1670 | 0 | } |
1671 | | |
1672 | 0 | return poCC; |
1673 | 0 | } |
1674 | | |
1675 | | /* -------------------------------------------------------------------- */ |
1676 | | /* Circle */ |
1677 | | /* -------------------------------------------------------------------- */ |
1678 | 0 | if (EQUAL(pszBaseGeometry, "Circle")) |
1679 | 0 | { |
1680 | 0 | auto poLine = std::make_unique<OGRLineString>(); |
1681 | |
|
1682 | 0 | if (!ParseGMLCoordinates(psNode, poLine.get(), nSRSDimension)) |
1683 | 0 | { |
1684 | 0 | return nullptr; |
1685 | 0 | } |
1686 | | |
1687 | 0 | if (poLine->getNumPoints() != 3) |
1688 | 0 | { |
1689 | 0 | ReportFailure("Bad number of points in Circle"); |
1690 | 0 | return nullptr; |
1691 | 0 | } |
1692 | | |
1693 | 0 | double R = 0.0; |
1694 | 0 | double cx = 0.0; |
1695 | 0 | double cy = 0.0; |
1696 | 0 | double alpha0 = 0.0; |
1697 | 0 | double alpha1 = 0.0; |
1698 | 0 | double alpha2 = 0.0; |
1699 | 0 | if (!OGRGeometryFactory::GetCurveParameters( |
1700 | 0 | poLine->getX(0), poLine->getY(0), poLine->getX(1), |
1701 | 0 | poLine->getY(1), poLine->getX(2), poLine->getY(2), R, cx, cy, |
1702 | 0 | alpha0, alpha1, alpha2)) |
1703 | 0 | { |
1704 | 0 | return nullptr; |
1705 | 0 | } |
1706 | | |
1707 | 0 | auto poCC = std::make_unique<OGRCircularString>(); |
1708 | 0 | OGRPoint p; |
1709 | 0 | poLine->getPoint(0, &p); |
1710 | 0 | poCC->addPoint(&p); |
1711 | 0 | poLine->getPoint(1, &p); |
1712 | 0 | poCC->addPoint(&p); |
1713 | 0 | poLine->getPoint(2, &p); |
1714 | 0 | poCC->addPoint(&p); |
1715 | 0 | const double alpha4 = |
1716 | 0 | alpha2 > alpha0 ? alpha0 + kdf2PI : alpha0 - kdf2PI; |
1717 | 0 | const double alpha3 = (alpha2 + alpha4) / 2.0; |
1718 | 0 | const double x = cx + R * cos(alpha3); |
1719 | 0 | const double y = cy + R * sin(alpha3); |
1720 | 0 | if (poCC->getCoordinateDimension() == 3) |
1721 | 0 | poCC->addPoint(x, y, p.getZ()); |
1722 | 0 | else |
1723 | 0 | poCC->addPoint(x, y); |
1724 | 0 | poLine->getPoint(0, &p); |
1725 | 0 | poCC->addPoint(&p); |
1726 | 0 | return poCC; |
1727 | 0 | } |
1728 | | |
1729 | | /* -------------------------------------------------------------------- */ |
1730 | | /* ArcByBulge */ |
1731 | | /* -------------------------------------------------------------------- */ |
1732 | 0 | if (EQUAL(pszBaseGeometry, "ArcByBulge")) |
1733 | 0 | { |
1734 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "bulge"); |
1735 | 0 | if (psChild == nullptr || psChild->eType != CXT_Element || |
1736 | 0 | psChild->psChild == nullptr) |
1737 | 0 | { |
1738 | 0 | ReportFailure("Missing bulge element."); |
1739 | 0 | return nullptr; |
1740 | 0 | } |
1741 | 0 | const double dfBulge = CPLAtof(psChild->psChild->pszValue); |
1742 | |
|
1743 | 0 | psChild = FindBareXMLChild(psNode, "normal"); |
1744 | 0 | if (psChild == nullptr || psChild->eType != CXT_Element) |
1745 | 0 | { |
1746 | 0 | ReportFailure("Missing normal element."); |
1747 | 0 | return nullptr; |
1748 | 0 | } |
1749 | 0 | double dfNormal = CPLAtof(psChild->psChild->pszValue); |
1750 | |
|
1751 | 0 | auto poLS = std::make_unique<OGRLineString>(); |
1752 | 0 | if (!ParseGMLCoordinates(psNode, poLS.get(), nSRSDimension)) |
1753 | 0 | { |
1754 | 0 | return nullptr; |
1755 | 0 | } |
1756 | | |
1757 | 0 | if (poLS->getNumPoints() != 2) |
1758 | 0 | { |
1759 | 0 | ReportFailure("Bad number of points in ArcByBulge"); |
1760 | 0 | return nullptr; |
1761 | 0 | } |
1762 | | |
1763 | 0 | auto poCC = std::make_unique<OGRCircularString>(); |
1764 | 0 | OGRPoint p; |
1765 | 0 | poLS->getPoint(0, &p); |
1766 | 0 | poCC->addPoint(&p); |
1767 | |
|
1768 | 0 | const double dfMidX = (poLS->getX(0) + poLS->getX(1)) / 2.0; |
1769 | 0 | const double dfMidY = (poLS->getY(0) + poLS->getY(1)) / 2.0; |
1770 | 0 | const double dfDirX = (poLS->getX(1) - poLS->getX(0)) / 2.0; |
1771 | 0 | const double dfDirY = (poLS->getY(1) - poLS->getY(0)) / 2.0; |
1772 | 0 | double dfNormX = -dfDirY; |
1773 | 0 | double dfNormY = dfDirX; |
1774 | 0 | const double dfNorm = sqrt(dfNormX * dfNormX + dfNormY * dfNormY); |
1775 | 0 | if (dfNorm != 0.0) |
1776 | 0 | { |
1777 | 0 | dfNormX /= dfNorm; |
1778 | 0 | dfNormY /= dfNorm; |
1779 | 0 | } |
1780 | 0 | const double dfNewX = dfMidX + dfNormX * dfBulge * dfNormal; |
1781 | 0 | const double dfNewY = dfMidY + dfNormY * dfBulge * dfNormal; |
1782 | |
|
1783 | 0 | if (poCC->getCoordinateDimension() == 3) |
1784 | 0 | poCC->addPoint(dfNewX, dfNewY, p.getZ()); |
1785 | 0 | else |
1786 | 0 | poCC->addPoint(dfNewX, dfNewY); |
1787 | |
|
1788 | 0 | poLS->getPoint(1, &p); |
1789 | 0 | poCC->addPoint(&p); |
1790 | |
|
1791 | 0 | return poCC; |
1792 | 0 | } |
1793 | | |
1794 | | /* -------------------------------------------------------------------- */ |
1795 | | /* ArcByCenterPoint */ |
1796 | | /* -------------------------------------------------------------------- */ |
1797 | 0 | if (EQUAL(pszBaseGeometry, "ArcByCenterPoint")) |
1798 | 0 | { |
1799 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "radius"); |
1800 | 0 | if (psChild == nullptr || psChild->eType != CXT_Element) |
1801 | 0 | { |
1802 | 0 | ReportFailure("Missing radius element."); |
1803 | 0 | return nullptr; |
1804 | 0 | } |
1805 | 0 | const char *pszUnits = CPLGetXMLValue(psChild, "uom", nullptr); |
1806 | 0 | const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId); |
1807 | 0 | const double dfRadiusRaw = |
1808 | 0 | CPLAtof(CPLGetXMLValue(psChild, nullptr, "0")); |
1809 | 0 | double dfRadius = dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw; |
1810 | |
|
1811 | 0 | psChild = FindBareXMLChild(psNode, "startAngle"); |
1812 | 0 | if (psChild == nullptr || psChild->eType != CXT_Element) |
1813 | 0 | { |
1814 | 0 | ReportFailure("Missing startAngle element."); |
1815 | 0 | return nullptr; |
1816 | 0 | } |
1817 | 0 | const double dfStartAngle = |
1818 | 0 | CPLAtof(CPLGetXMLValue(psChild, nullptr, "0")); |
1819 | |
|
1820 | 0 | psChild = FindBareXMLChild(psNode, "endAngle"); |
1821 | 0 | if (psChild == nullptr || psChild->eType != CXT_Element) |
1822 | 0 | { |
1823 | 0 | ReportFailure("Missing endAngle element."); |
1824 | 0 | return nullptr; |
1825 | 0 | } |
1826 | 0 | const double dfEndAngle = |
1827 | 0 | CPLAtof(CPLGetXMLValue(psChild, nullptr, "0")); |
1828 | |
|
1829 | 0 | OGRPoint p; |
1830 | 0 | if (!ParseGMLCoordinates(psNode, &p, nSRSDimension)) |
1831 | 0 | { |
1832 | 0 | return nullptr; |
1833 | 0 | } |
1834 | | |
1835 | 0 | bool bSRSUnitIsDegree = false; |
1836 | 0 | bool bInvertedAxisOrder = false; |
1837 | 0 | double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS; |
1838 | 0 | if (pszSRSName != nullptr) |
1839 | 0 | { |
1840 | 0 | OGRSpatialReference oSRS; |
1841 | 0 | if (oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE) |
1842 | 0 | { |
1843 | 0 | if (oSRS.IsGeographic()) |
1844 | 0 | { |
1845 | 0 | dfSemiMajor = GetSemiMajor(&oSRS); |
1846 | 0 | bInvertedAxisOrder = |
1847 | 0 | CPL_TO_BOOL(oSRS.EPSGTreatsAsLatLong()); |
1848 | 0 | bSRSUnitIsDegree = fabs(oSRS.GetAngularUnits(nullptr) - |
1849 | 0 | CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8; |
1850 | 0 | } |
1851 | 0 | else if (oSRS.IsProjected()) |
1852 | 0 | { |
1853 | 0 | dfSemiMajor = GetSemiMajor(&oSRS); |
1854 | 0 | bInvertedAxisOrder = |
1855 | 0 | CPL_TO_BOOL(oSRS.EPSGTreatsAsNorthingEasting()); |
1856 | |
|
1857 | 0 | const double dfSRSUnitsToMetre = |
1858 | 0 | oSRS.GetLinearUnits(nullptr); |
1859 | 0 | if (dfSRSUnitsToMetre > 0) |
1860 | 0 | dfRadius /= dfSRSUnitsToMetre; |
1861 | 0 | } |
1862 | 0 | } |
1863 | 0 | } |
1864 | |
|
1865 | 0 | double dfCenterX = p.getX(); |
1866 | 0 | double dfCenterY = p.getY(); |
1867 | |
|
1868 | 0 | if (bSRSUnitIsDegree && dfUOMConv > 0) |
1869 | 0 | { |
1870 | 0 | auto poLS = std::make_unique<OGRLineString>(); |
1871 | 0 | const double dfStep = OGRGeometryFactory::GetDefaultArcStepSize(); |
1872 | 0 | const double dfSign = dfStartAngle < dfEndAngle ? 1 : -1; |
1873 | 0 | for (double dfAngle = dfStartAngle; |
1874 | 0 | (dfAngle - dfEndAngle) * dfSign < 0; |
1875 | 0 | dfAngle += dfSign * dfStep) |
1876 | 0 | { |
1877 | 0 | double dfLong = 0.0; |
1878 | 0 | double dfLat = 0.0; |
1879 | 0 | if (bInvertedAxisOrder) |
1880 | 0 | { |
1881 | 0 | OGR_GreatCircle_ExtendPosition( |
1882 | 0 | dfCenterX, dfCenterY, dfRadius, |
1883 | | // See |
1884 | | // https://ext.eurocontrol.int/aixm_confluence/display/ACG/ArcByCenterPoint+Interpretation+Summary |
1885 | 0 | dfAngle, dfSemiMajor, &dfLat, &dfLong); |
1886 | 0 | p.setX(dfLat); // yes, external code will do the swap later |
1887 | 0 | p.setY(dfLong); |
1888 | 0 | } |
1889 | 0 | else |
1890 | 0 | { |
1891 | 0 | OGR_GreatCircle_ExtendPosition( |
1892 | 0 | dfCenterY, dfCenterX, dfRadius, 90 - dfAngle, |
1893 | 0 | dfSemiMajor, &dfLat, &dfLong); |
1894 | 0 | p.setX(dfLong); |
1895 | 0 | p.setY(dfLat); |
1896 | 0 | } |
1897 | 0 | poLS->addPoint(&p); |
1898 | 0 | } |
1899 | |
|
1900 | 0 | double dfLong = 0.0; |
1901 | 0 | double dfLat = 0.0; |
1902 | 0 | if (bInvertedAxisOrder) |
1903 | 0 | { |
1904 | 0 | OGR_GreatCircle_ExtendPosition(dfCenterX, dfCenterY, dfRadius, |
1905 | 0 | dfEndAngle, dfSemiMajor, &dfLat, |
1906 | 0 | &dfLong); |
1907 | 0 | p.setX(dfLat); // yes, external code will do the swap later |
1908 | 0 | p.setY(dfLong); |
1909 | 0 | } |
1910 | 0 | else |
1911 | 0 | { |
1912 | 0 | OGR_GreatCircle_ExtendPosition(dfCenterY, dfCenterX, dfRadius, |
1913 | 0 | 90 - dfEndAngle, dfSemiMajor, |
1914 | 0 | &dfLat, &dfLong); |
1915 | 0 | p.setX(dfLong); |
1916 | 0 | p.setY(dfLat); |
1917 | 0 | } |
1918 | 0 | poLS->addPoint(&p); |
1919 | |
|
1920 | 0 | return poLS; |
1921 | 0 | } |
1922 | | |
1923 | 0 | if (bInvertedAxisOrder) |
1924 | 0 | std::swap(dfCenterX, dfCenterY); |
1925 | |
|
1926 | 0 | auto poCC = std::make_unique<OGRCircularString>(); |
1927 | 0 | p.setX(dfCenterX + dfRadius * cos(dfStartAngle * kdfD2R)); |
1928 | 0 | p.setY(dfCenterY + dfRadius * sin(dfStartAngle * kdfD2R)); |
1929 | 0 | poCC->addPoint(&p); |
1930 | 0 | const double dfAverageAngle = (dfStartAngle + dfEndAngle) / 2.0; |
1931 | 0 | p.setX(dfCenterX + dfRadius * cos(dfAverageAngle * kdfD2R)); |
1932 | 0 | p.setY(dfCenterY + dfRadius * sin(dfAverageAngle * kdfD2R)); |
1933 | 0 | poCC->addPoint(&p); |
1934 | 0 | p.setX(dfCenterX + dfRadius * cos(dfEndAngle * kdfD2R)); |
1935 | 0 | p.setY(dfCenterY + dfRadius * sin(dfEndAngle * kdfD2R)); |
1936 | 0 | poCC->addPoint(&p); |
1937 | |
|
1938 | 0 | if (bInvertedAxisOrder) |
1939 | 0 | poCC->swapXY(); |
1940 | |
|
1941 | 0 | return poCC; |
1942 | 0 | } |
1943 | | |
1944 | | /* -------------------------------------------------------------------- */ |
1945 | | /* CircleByCenterPoint */ |
1946 | | /* -------------------------------------------------------------------- */ |
1947 | 0 | if (EQUAL(pszBaseGeometry, "CircleByCenterPoint")) |
1948 | 0 | { |
1949 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "radius"); |
1950 | 0 | if (psChild == nullptr || psChild->eType != CXT_Element) |
1951 | 0 | { |
1952 | 0 | ReportFailure("Missing radius element."); |
1953 | 0 | return nullptr; |
1954 | 0 | } |
1955 | 0 | const char *pszUnits = CPLGetXMLValue(psChild, "uom", nullptr); |
1956 | 0 | const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId); |
1957 | 0 | const double dfRadiusRaw = |
1958 | 0 | CPLAtof(CPLGetXMLValue(psChild, nullptr, "0")); |
1959 | 0 | double dfRadius = dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw; |
1960 | |
|
1961 | 0 | OGRPoint p; |
1962 | 0 | if (!ParseGMLCoordinates(psNode, &p, nSRSDimension)) |
1963 | 0 | { |
1964 | 0 | return nullptr; |
1965 | 0 | } |
1966 | | |
1967 | 0 | bool bSRSUnitIsDegree = false; |
1968 | 0 | bool bInvertedAxisOrder = false; |
1969 | 0 | double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS; |
1970 | 0 | if (pszSRSName != nullptr) |
1971 | 0 | { |
1972 | 0 | OGRSpatialReference oSRS; |
1973 | 0 | if (oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE) |
1974 | 0 | { |
1975 | 0 | if (oSRS.IsGeographic()) |
1976 | 0 | { |
1977 | 0 | dfSemiMajor = GetSemiMajor(&oSRS); |
1978 | 0 | bInvertedAxisOrder = |
1979 | 0 | CPL_TO_BOOL(oSRS.EPSGTreatsAsLatLong()); |
1980 | 0 | bSRSUnitIsDegree = fabs(oSRS.GetAngularUnits(nullptr) - |
1981 | 0 | CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8; |
1982 | 0 | } |
1983 | 0 | else if (oSRS.IsProjected()) |
1984 | 0 | { |
1985 | 0 | dfSemiMajor = GetSemiMajor(&oSRS); |
1986 | 0 | bInvertedAxisOrder = |
1987 | 0 | CPL_TO_BOOL(oSRS.EPSGTreatsAsNorthingEasting()); |
1988 | |
|
1989 | 0 | const double dfSRSUnitsToMetre = |
1990 | 0 | oSRS.GetLinearUnits(nullptr); |
1991 | 0 | if (dfSRSUnitsToMetre > 0) |
1992 | 0 | dfRadius /= dfSRSUnitsToMetre; |
1993 | 0 | } |
1994 | 0 | } |
1995 | 0 | } |
1996 | |
|
1997 | 0 | double dfCenterX = p.getX(); |
1998 | 0 | double dfCenterY = p.getY(); |
1999 | |
|
2000 | 0 | if (bSRSUnitIsDegree && dfUOMConv > 0) |
2001 | 0 | { |
2002 | 0 | auto poLS = std::make_unique<OGRLineString>(); |
2003 | 0 | const double dfStep = OGRGeometryFactory::GetDefaultArcStepSize(); |
2004 | 0 | for (double dfAngle = 0; dfAngle < 360; dfAngle += dfStep) |
2005 | 0 | { |
2006 | 0 | double dfLong = 0.0; |
2007 | 0 | double dfLat = 0.0; |
2008 | 0 | if (bInvertedAxisOrder) |
2009 | 0 | { |
2010 | 0 | OGR_GreatCircle_ExtendPosition( |
2011 | 0 | dfCenterX, dfCenterY, dfRadius, dfAngle, dfSemiMajor, |
2012 | 0 | &dfLat, &dfLong); |
2013 | 0 | p.setX(dfLat); // yes, external code will do the swap later |
2014 | 0 | p.setY(dfLong); |
2015 | 0 | } |
2016 | 0 | else |
2017 | 0 | { |
2018 | 0 | OGR_GreatCircle_ExtendPosition( |
2019 | 0 | dfCenterY, dfCenterX, dfRadius, dfAngle, dfSemiMajor, |
2020 | 0 | &dfLat, &dfLong); |
2021 | 0 | p.setX(dfLong); |
2022 | 0 | p.setY(dfLat); |
2023 | 0 | } |
2024 | 0 | poLS->addPoint(&p); |
2025 | 0 | } |
2026 | 0 | poLS->getPoint(0, &p); |
2027 | 0 | poLS->addPoint(&p); |
2028 | 0 | return poLS; |
2029 | 0 | } |
2030 | | |
2031 | 0 | if (bInvertedAxisOrder) |
2032 | 0 | std::swap(dfCenterX, dfCenterY); |
2033 | |
|
2034 | 0 | auto poCC = std::make_unique<OGRCircularString>(); |
2035 | 0 | p.setX(dfCenterX - dfRadius); |
2036 | 0 | p.setY(dfCenterY); |
2037 | 0 | poCC->addPoint(&p); |
2038 | 0 | p.setX(dfCenterX); |
2039 | 0 | p.setY(dfCenterY + dfRadius); |
2040 | 0 | poCC->addPoint(&p); |
2041 | 0 | p.setX(dfCenterX + dfRadius); |
2042 | 0 | p.setY(dfCenterY); |
2043 | 0 | poCC->addPoint(&p); |
2044 | 0 | p.setX(dfCenterX); |
2045 | 0 | p.setY(dfCenterY - dfRadius); |
2046 | 0 | poCC->addPoint(&p); |
2047 | 0 | p.setX(dfCenterX - dfRadius); |
2048 | 0 | p.setY(dfCenterY); |
2049 | 0 | poCC->addPoint(&p); |
2050 | |
|
2051 | 0 | if (bInvertedAxisOrder) |
2052 | 0 | poCC->swapXY(); |
2053 | |
|
2054 | 0 | return poCC; |
2055 | 0 | } |
2056 | | |
2057 | | /* -------------------------------------------------------------------- */ |
2058 | | /* PointType */ |
2059 | | /* -------------------------------------------------------------------- */ |
2060 | 0 | if (EQUAL(pszBaseGeometry, "PointType") || |
2061 | 0 | EQUAL(pszBaseGeometry, "Point") || |
2062 | 0 | EQUAL(pszBaseGeometry, "ElevatedPoint") || |
2063 | 0 | EQUAL(pszBaseGeometry, "ConnectionPoint")) |
2064 | 0 | { |
2065 | 0 | auto poPoint = std::make_unique<OGRPoint>(); |
2066 | |
|
2067 | 0 | if (!ParseGMLCoordinates(psNode, poPoint.get(), nSRSDimension)) |
2068 | 0 | { |
2069 | 0 | return nullptr; |
2070 | 0 | } |
2071 | | |
2072 | 0 | return poPoint; |
2073 | 0 | } |
2074 | | |
2075 | | /* -------------------------------------------------------------------- */ |
2076 | | /* Box */ |
2077 | | /* -------------------------------------------------------------------- */ |
2078 | 0 | if (EQUAL(pszBaseGeometry, "BoxType") || EQUAL(pszBaseGeometry, "Box")) |
2079 | 0 | { |
2080 | 0 | OGRLineString oPoints; |
2081 | |
|
2082 | 0 | if (!ParseGMLCoordinates(psNode, &oPoints, nSRSDimension)) |
2083 | 0 | return nullptr; |
2084 | | |
2085 | 0 | if (oPoints.getNumPoints() < 2) |
2086 | 0 | return nullptr; |
2087 | | |
2088 | 0 | auto poBoxRing = std::make_unique<OGRLinearRing>(); |
2089 | 0 | auto poBoxPoly = std::make_unique<OGRPolygon>(); |
2090 | |
|
2091 | 0 | poBoxRing->setNumPoints(5); |
2092 | 0 | poBoxRing->setPoint(0, oPoints.getX(0), oPoints.getY(0), |
2093 | 0 | oPoints.getZ(0)); |
2094 | 0 | poBoxRing->setPoint(1, oPoints.getX(1), oPoints.getY(0), |
2095 | 0 | oPoints.getZ(0)); |
2096 | 0 | poBoxRing->setPoint(2, oPoints.getX(1), oPoints.getY(1), |
2097 | 0 | oPoints.getZ(1)); |
2098 | 0 | poBoxRing->setPoint(3, oPoints.getX(0), oPoints.getY(1), |
2099 | 0 | oPoints.getZ(0)); |
2100 | 0 | poBoxRing->setPoint(4, oPoints.getX(0), oPoints.getY(0), |
2101 | 0 | oPoints.getZ(0)); |
2102 | 0 | poBoxRing->set3D(oPoints.Is3D()); |
2103 | |
|
2104 | 0 | poBoxPoly->addRing(std::move(poBoxRing)); |
2105 | |
|
2106 | 0 | return poBoxPoly; |
2107 | 0 | } |
2108 | | |
2109 | | /* -------------------------------------------------------------------- */ |
2110 | | /* Envelope */ |
2111 | | /* -------------------------------------------------------------------- */ |
2112 | 0 | if (EQUAL(pszBaseGeometry, "Envelope")) |
2113 | 0 | { |
2114 | 0 | const CPLXMLNode *psLowerCorner = |
2115 | 0 | FindBareXMLChild(psNode, "lowerCorner"); |
2116 | 0 | const CPLXMLNode *psUpperCorner = |
2117 | 0 | FindBareXMLChild(psNode, "upperCorner"); |
2118 | 0 | if (psLowerCorner == nullptr || psUpperCorner == nullptr) |
2119 | 0 | return nullptr; |
2120 | 0 | const char *pszLowerCorner = GetElementText(psLowerCorner); |
2121 | 0 | const char *pszUpperCorner = GetElementText(psUpperCorner); |
2122 | 0 | if (pszLowerCorner == nullptr || pszUpperCorner == nullptr) |
2123 | 0 | return nullptr; |
2124 | 0 | char **papszLowerCorner = CSLTokenizeString(pszLowerCorner); |
2125 | 0 | char **papszUpperCorner = CSLTokenizeString(pszUpperCorner); |
2126 | 0 | const int nTokenCountLC = CSLCount(papszLowerCorner); |
2127 | 0 | const int nTokenCountUC = CSLCount(papszUpperCorner); |
2128 | 0 | if (nTokenCountLC < 2 || nTokenCountUC < 2) |
2129 | 0 | { |
2130 | 0 | CSLDestroy(papszLowerCorner); |
2131 | 0 | CSLDestroy(papszUpperCorner); |
2132 | 0 | return nullptr; |
2133 | 0 | } |
2134 | | |
2135 | 0 | const double dfLLX = CPLAtof(papszLowerCorner[0]); |
2136 | 0 | const double dfLLY = CPLAtof(papszLowerCorner[1]); |
2137 | 0 | const double dfURX = CPLAtof(papszUpperCorner[0]); |
2138 | 0 | const double dfURY = CPLAtof(papszUpperCorner[1]); |
2139 | 0 | CSLDestroy(papszLowerCorner); |
2140 | 0 | CSLDestroy(papszUpperCorner); |
2141 | |
|
2142 | 0 | auto poEnvelopeRing = std::make_unique<OGRLinearRing>(); |
2143 | 0 | auto poPoly = std::make_unique<OGRPolygon>(); |
2144 | |
|
2145 | 0 | poEnvelopeRing->setNumPoints(5); |
2146 | 0 | poEnvelopeRing->setPoint(0, dfLLX, dfLLY); |
2147 | 0 | poEnvelopeRing->setPoint(1, dfURX, dfLLY); |
2148 | 0 | poEnvelopeRing->setPoint(2, dfURX, dfURY); |
2149 | 0 | poEnvelopeRing->setPoint(3, dfLLX, dfURY); |
2150 | 0 | poEnvelopeRing->setPoint(4, dfLLX, dfLLY); |
2151 | 0 | poPoly->addRing(std::move(poEnvelopeRing)); |
2152 | |
|
2153 | 0 | return poPoly; |
2154 | 0 | } |
2155 | | |
2156 | | /* --------------------------------------------------------------------- */ |
2157 | | /* MultiPolygon / MultiSurface / CompositeSurface */ |
2158 | | /* */ |
2159 | | /* For CompositeSurface, this is a very rough approximation to deal with */ |
2160 | | /* it as a MultiPolygon, because it can several faces of a 3D volume. */ |
2161 | | /* --------------------------------------------------------------------- */ |
2162 | 0 | if (EQUAL(pszBaseGeometry, "MultiPolygon") || |
2163 | 0 | EQUAL(pszBaseGeometry, "MultiSurface") || |
2164 | 0 | EQUAL(pszBaseGeometry, "CompositeSurface")) |
2165 | 0 | { |
2166 | 0 | std::unique_ptr<OGRMultiSurface> poMS = |
2167 | 0 | EQUAL(pszBaseGeometry, "MultiPolygon") |
2168 | 0 | ? std::make_unique<OGRMultiPolygon>() |
2169 | 0 | : std::make_unique<OGRMultiSurface>(); |
2170 | 0 | bool bReconstructTopology = false; |
2171 | 0 | bool bChildrenAreAllPolygons = true; |
2172 | | |
2173 | | // Iterate over children. |
2174 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
2175 | 0 | psChild = psChild->psNext) |
2176 | 0 | { |
2177 | 0 | const char *pszMemberElement = BareGMLElement(psChild->pszValue); |
2178 | 0 | if (psChild->eType == CXT_Element && |
2179 | 0 | (EQUAL(pszMemberElement, "polygonMember") || |
2180 | 0 | EQUAL(pszMemberElement, "surfaceMember"))) |
2181 | 0 | { |
2182 | 0 | const CPLXMLNode *psSurfaceChild = GetChildElement(psChild); |
2183 | |
|
2184 | 0 | if (psSurfaceChild != nullptr) |
2185 | 0 | { |
2186 | | // Cf #5421 where there are PolygonPatch with only inner |
2187 | | // rings. |
2188 | 0 | const CPLXMLNode *psPolygonPatch = |
2189 | 0 | GetChildElement(GetChildElement(psSurfaceChild)); |
2190 | 0 | const CPLXMLNode *psPolygonPatchChild = nullptr; |
2191 | 0 | if (psPolygonPatch != nullptr && |
2192 | 0 | psPolygonPatch->eType == CXT_Element && |
2193 | 0 | EQUAL(BareGMLElement(psPolygonPatch->pszValue), |
2194 | 0 | "PolygonPatch") && |
2195 | 0 | (psPolygonPatchChild = |
2196 | 0 | GetChildElement(psPolygonPatch)) != nullptr && |
2197 | 0 | EQUAL(BareGMLElement(psPolygonPatchChild->pszValue), |
2198 | 0 | "interior")) |
2199 | 0 | { |
2200 | | // Find all inner rings |
2201 | 0 | for (const CPLXMLNode *psChild2 = |
2202 | 0 | psPolygonPatch->psChild; |
2203 | 0 | psChild2 != nullptr; psChild2 = psChild2->psNext) |
2204 | 0 | { |
2205 | 0 | if (psChild2->eType == CXT_Element && |
2206 | 0 | (EQUAL(BareGMLElement(psChild2->pszValue), |
2207 | 0 | "interior"))) |
2208 | 0 | { |
2209 | 0 | const CPLXMLNode *psInteriorChild = |
2210 | 0 | GetChildElement(psChild2); |
2211 | 0 | auto poRing = |
2212 | 0 | psInteriorChild == nullptr |
2213 | 0 | ? nullptr |
2214 | 0 | : GML2OGRGeometry_XMLNode_Internal( |
2215 | 0 | psInteriorChild, pszId, |
2216 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
2217 | 0 | nRecLevel + 1, nSRSDimension, |
2218 | 0 | pszSRSName); |
2219 | 0 | if (poRing == nullptr) |
2220 | 0 | { |
2221 | 0 | ReportFailure("Invalid interior ring"); |
2222 | 0 | return nullptr; |
2223 | 0 | } |
2224 | 0 | if (!EQUAL(poRing->getGeometryName(), |
2225 | 0 | "LINEARRING")) |
2226 | 0 | { |
2227 | 0 | ReportFailure("%s: Got %s geometry as " |
2228 | 0 | "innerBoundaryIs instead of " |
2229 | 0 | "LINEARRING.", |
2230 | 0 | pszBaseGeometry, |
2231 | 0 | poRing->getGeometryName()); |
2232 | 0 | return nullptr; |
2233 | 0 | } |
2234 | | |
2235 | 0 | bReconstructTopology = true; |
2236 | 0 | auto poPolygon = std::make_unique<OGRPolygon>(); |
2237 | 0 | auto poLinearRing = |
2238 | 0 | std::unique_ptr<OGRLinearRing>( |
2239 | 0 | poRing.release()->toLinearRing()); |
2240 | 0 | poPolygon->addRing(std::move(poLinearRing)); |
2241 | 0 | poMS->addGeometry(std::move(poPolygon)); |
2242 | 0 | } |
2243 | 0 | } |
2244 | 0 | } |
2245 | 0 | else |
2246 | 0 | { |
2247 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2248 | 0 | psSurfaceChild, pszId, |
2249 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
2250 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2251 | 0 | if (!GML2OGRGeometry_AddToMultiSurface( |
2252 | 0 | poMS.get(), std::move(poGeom), pszMemberElement, |
2253 | 0 | bChildrenAreAllPolygons)) |
2254 | 0 | { |
2255 | 0 | return nullptr; |
2256 | 0 | } |
2257 | 0 | } |
2258 | 0 | } |
2259 | 0 | } |
2260 | 0 | else if (psChild->eType == CXT_Element && |
2261 | 0 | EQUAL(pszMemberElement, "surfaceMembers")) |
2262 | 0 | { |
2263 | 0 | for (const CPLXMLNode *psChild2 = psChild->psChild; |
2264 | 0 | psChild2 != nullptr; psChild2 = psChild2->psNext) |
2265 | 0 | { |
2266 | 0 | pszMemberElement = BareGMLElement(psChild2->pszValue); |
2267 | 0 | if (psChild2->eType == CXT_Element && |
2268 | 0 | (EQUAL(pszMemberElement, "Surface") || |
2269 | 0 | EQUAL(pszMemberElement, "Polygon") || |
2270 | 0 | EQUAL(pszMemberElement, "PolygonPatch") || |
2271 | 0 | EQUAL(pszMemberElement, "CompositeSurface"))) |
2272 | 0 | { |
2273 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2274 | 0 | psChild2, pszId, |
2275 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
2276 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2277 | 0 | if (!GML2OGRGeometry_AddToMultiSurface( |
2278 | 0 | poMS.get(), std::move(poGeom), pszMemberElement, |
2279 | 0 | bChildrenAreAllPolygons)) |
2280 | 0 | { |
2281 | 0 | return nullptr; |
2282 | 0 | } |
2283 | 0 | } |
2284 | 0 | } |
2285 | 0 | } |
2286 | 0 | } |
2287 | | |
2288 | 0 | if (bReconstructTopology && bChildrenAreAllPolygons) |
2289 | 0 | { |
2290 | 0 | auto poMPoly = |
2291 | 0 | wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface |
2292 | 0 | ? std::unique_ptr<OGRMultiPolygon>( |
2293 | 0 | OGRMultiSurface::CastToMultiPolygon(poMS.release())) |
2294 | 0 | : std::unique_ptr<OGRMultiPolygon>( |
2295 | 0 | poMS.release()->toMultiPolygon()); |
2296 | 0 | const int nPolygonCount = poMPoly->getNumGeometries(); |
2297 | 0 | std::vector<OGRGeometry *> apoPolygons; |
2298 | 0 | apoPolygons.reserve(nPolygonCount); |
2299 | 0 | for (int i = 0; i < nPolygonCount; i++) |
2300 | 0 | { |
2301 | 0 | apoPolygons.emplace_back(poMPoly->getGeometryRef(0)); |
2302 | 0 | poMPoly->removeGeometry(0, FALSE); |
2303 | 0 | } |
2304 | 0 | poMPoly.reset(); |
2305 | 0 | int bResultValidGeometry = FALSE; |
2306 | 0 | return std::unique_ptr<OGRGeometry>( |
2307 | 0 | OGRGeometryFactory::organizePolygons( |
2308 | 0 | apoPolygons.data(), nPolygonCount, &bResultValidGeometry)); |
2309 | 0 | } |
2310 | 0 | else |
2311 | 0 | { |
2312 | 0 | if (/* bCastToLinearTypeIfPossible && */ |
2313 | 0 | wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface && |
2314 | 0 | bChildrenAreAllPolygons) |
2315 | 0 | { |
2316 | 0 | return std::unique_ptr<OGRMultiPolygon>( |
2317 | 0 | OGRMultiSurface::CastToMultiPolygon(poMS.release())); |
2318 | 0 | } |
2319 | | |
2320 | 0 | return poMS; |
2321 | 0 | } |
2322 | 0 | } |
2323 | | |
2324 | | /* -------------------------------------------------------------------- */ |
2325 | | /* MultiPoint */ |
2326 | | /* -------------------------------------------------------------------- */ |
2327 | 0 | if (EQUAL(pszBaseGeometry, "MultiPoint")) |
2328 | 0 | { |
2329 | 0 | auto poMP = std::make_unique<OGRMultiPoint>(); |
2330 | | |
2331 | | // Collect points. |
2332 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
2333 | 0 | psChild = psChild->psNext) |
2334 | 0 | { |
2335 | 0 | if (psChild->eType == CXT_Element && |
2336 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "pointMember")) |
2337 | 0 | { |
2338 | 0 | const CPLXMLNode *psPointChild = GetChildElement(psChild); |
2339 | |
|
2340 | 0 | if (psPointChild != nullptr) |
2341 | 0 | { |
2342 | 0 | auto poPointMember = GML2OGRGeometry_XMLNode_Internal( |
2343 | 0 | psPointChild, pszId, |
2344 | 0 | nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1, |
2345 | 0 | nSRSDimension, pszSRSName); |
2346 | 0 | if (poPointMember == nullptr || |
2347 | 0 | wkbFlatten(poPointMember->getGeometryType()) != |
2348 | 0 | wkbPoint) |
2349 | 0 | { |
2350 | 0 | ReportFailure("MultiPoint: Got %s geometry as " |
2351 | 0 | "pointMember instead of POINT", |
2352 | 0 | poPointMember |
2353 | 0 | ? poPointMember->getGeometryName() |
2354 | 0 | : "NULL"); |
2355 | 0 | return nullptr; |
2356 | 0 | } |
2357 | | |
2358 | 0 | poMP->addGeometry(std::move(poPointMember)); |
2359 | 0 | } |
2360 | 0 | } |
2361 | 0 | else if (psChild->eType == CXT_Element && |
2362 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "pointMembers")) |
2363 | 0 | { |
2364 | 0 | for (const CPLXMLNode *psChild2 = psChild->psChild; |
2365 | 0 | psChild2 != nullptr; psChild2 = psChild2->psNext) |
2366 | 0 | { |
2367 | 0 | if (psChild2->eType == CXT_Element && |
2368 | 0 | (EQUAL(BareGMLElement(psChild2->pszValue), "Point"))) |
2369 | 0 | { |
2370 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2371 | 0 | psChild2, pszId, |
2372 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
2373 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2374 | 0 | if (poGeom == nullptr) |
2375 | 0 | { |
2376 | 0 | ReportFailure("Invalid %s", |
2377 | 0 | BareGMLElement(psChild2->pszValue)); |
2378 | 0 | return nullptr; |
2379 | 0 | } |
2380 | | |
2381 | 0 | if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint) |
2382 | 0 | { |
2383 | 0 | auto poPoint = std::unique_ptr<OGRPoint>( |
2384 | 0 | poGeom.release()->toPoint()); |
2385 | 0 | poMP->addGeometry(std::move(poPoint)); |
2386 | 0 | } |
2387 | 0 | else |
2388 | 0 | { |
2389 | 0 | ReportFailure("Got %s geometry as pointMember " |
2390 | 0 | "instead of POINT.", |
2391 | 0 | poGeom->getGeometryName()); |
2392 | 0 | return nullptr; |
2393 | 0 | } |
2394 | 0 | } |
2395 | 0 | } |
2396 | 0 | } |
2397 | 0 | } |
2398 | | |
2399 | 0 | return poMP; |
2400 | 0 | } |
2401 | | |
2402 | | /* -------------------------------------------------------------------- */ |
2403 | | /* MultiLineString */ |
2404 | | /* -------------------------------------------------------------------- */ |
2405 | 0 | if (EQUAL(pszBaseGeometry, "MultiLineString")) |
2406 | 0 | { |
2407 | 0 | auto poMLS = std::make_unique<OGRMultiLineString>(); |
2408 | | |
2409 | | // Collect lines. |
2410 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
2411 | 0 | psChild = psChild->psNext) |
2412 | 0 | { |
2413 | 0 | if (psChild->eType == CXT_Element && |
2414 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "lineStringMember")) |
2415 | 0 | { |
2416 | 0 | const CPLXMLNode *psLineStringChild = GetChildElement(psChild); |
2417 | 0 | auto poGeom = |
2418 | 0 | psLineStringChild == nullptr |
2419 | 0 | ? nullptr |
2420 | 0 | : GML2OGRGeometry_XMLNode_Internal( |
2421 | 0 | psLineStringChild, pszId, |
2422 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
2423 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2424 | 0 | if (poGeom == nullptr || |
2425 | 0 | wkbFlatten(poGeom->getGeometryType()) != wkbLineString) |
2426 | 0 | { |
2427 | 0 | ReportFailure("MultiLineString: Got %s geometry as Member " |
2428 | 0 | "instead of LINESTRING.", |
2429 | 0 | poGeom ? poGeom->getGeometryName() : "NULL"); |
2430 | 0 | return nullptr; |
2431 | 0 | } |
2432 | | |
2433 | 0 | poMLS->addGeometry(std::move(poGeom)); |
2434 | 0 | } |
2435 | 0 | } |
2436 | | |
2437 | 0 | return poMLS; |
2438 | 0 | } |
2439 | | |
2440 | | /* -------------------------------------------------------------------- */ |
2441 | | /* MultiCurve */ |
2442 | | /* -------------------------------------------------------------------- */ |
2443 | 0 | if (EQUAL(pszBaseGeometry, "MultiCurve")) |
2444 | 0 | { |
2445 | 0 | auto poMC = std::make_unique<OGRMultiCurve>(); |
2446 | 0 | bool bChildrenAreAllLineString = true; |
2447 | | |
2448 | | // Collect curveMembers. |
2449 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
2450 | 0 | psChild = psChild->psNext) |
2451 | 0 | { |
2452 | 0 | if (psChild->eType == CXT_Element && |
2453 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "curveMember")) |
2454 | 0 | { |
2455 | 0 | const CPLXMLNode *psChild2 = GetChildElement(psChild); |
2456 | 0 | if (psChild2 != nullptr) // Empty curveMember is valid. |
2457 | 0 | { |
2458 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2459 | 0 | psChild2, pszId, nPseudoBoolGetSecondaryGeometryOption, |
2460 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2461 | 0 | if (poGeom == nullptr || |
2462 | 0 | !OGR_GT_IsCurve(poGeom->getGeometryType())) |
2463 | 0 | { |
2464 | 0 | ReportFailure("MultiCurve: Got %s geometry as Member " |
2465 | 0 | "instead of a curve.", |
2466 | 0 | poGeom ? poGeom->getGeometryName() |
2467 | 0 | : "NULL"); |
2468 | 0 | return nullptr; |
2469 | 0 | } |
2470 | | |
2471 | 0 | if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString) |
2472 | 0 | bChildrenAreAllLineString = false; |
2473 | |
|
2474 | 0 | if (poMC->addGeometry(std::move(poGeom)) != OGRERR_NONE) |
2475 | 0 | { |
2476 | 0 | return nullptr; |
2477 | 0 | } |
2478 | 0 | } |
2479 | 0 | } |
2480 | 0 | else if (psChild->eType == CXT_Element && |
2481 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "curveMembers")) |
2482 | 0 | { |
2483 | 0 | for (const CPLXMLNode *psChild2 = psChild->psChild; |
2484 | 0 | psChild2 != nullptr; psChild2 = psChild2->psNext) |
2485 | 0 | { |
2486 | 0 | if (psChild2->eType == CXT_Element) |
2487 | 0 | { |
2488 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2489 | 0 | psChild2, pszId, |
2490 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
2491 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2492 | 0 | if (poGeom == nullptr || |
2493 | 0 | !OGR_GT_IsCurve(poGeom->getGeometryType())) |
2494 | 0 | { |
2495 | 0 | ReportFailure("MultiCurve: Got %s geometry as " |
2496 | 0 | "Member instead of a curve.", |
2497 | 0 | poGeom ? poGeom->getGeometryName() |
2498 | 0 | : "NULL"); |
2499 | 0 | return nullptr; |
2500 | 0 | } |
2501 | | |
2502 | 0 | if (wkbFlatten(poGeom->getGeometryType()) != |
2503 | 0 | wkbLineString) |
2504 | 0 | bChildrenAreAllLineString = false; |
2505 | |
|
2506 | 0 | if (poMC->addGeometry(std::move(poGeom)) != OGRERR_NONE) |
2507 | 0 | { |
2508 | 0 | return nullptr; |
2509 | 0 | } |
2510 | 0 | } |
2511 | 0 | } |
2512 | 0 | } |
2513 | 0 | } |
2514 | | |
2515 | 0 | if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString) |
2516 | 0 | { |
2517 | 0 | return std::unique_ptr<OGRMultiLineString>( |
2518 | 0 | OGRMultiCurve::CastToMultiLineString(poMC.release())); |
2519 | 0 | } |
2520 | | |
2521 | 0 | return poMC; |
2522 | 0 | } |
2523 | | |
2524 | | /* -------------------------------------------------------------------- */ |
2525 | | /* CompositeCurve */ |
2526 | | /* -------------------------------------------------------------------- */ |
2527 | 0 | if (EQUAL(pszBaseGeometry, "CompositeCurve")) |
2528 | 0 | { |
2529 | 0 | auto poCC = std::make_unique<OGRCompoundCurve>(); |
2530 | 0 | bool bChildrenAreAllLineString = true; |
2531 | | |
2532 | | // Collect curveMembers. |
2533 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
2534 | 0 | psChild = psChild->psNext) |
2535 | 0 | { |
2536 | 0 | if (psChild->eType == CXT_Element && |
2537 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "curveMember")) |
2538 | 0 | { |
2539 | 0 | const CPLXMLNode *psChild2 = GetChildElement(psChild); |
2540 | 0 | if (psChild2 != nullptr) // Empty curveMember is valid. |
2541 | 0 | { |
2542 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2543 | 0 | psChild2, pszId, nPseudoBoolGetSecondaryGeometryOption, |
2544 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2545 | 0 | if (!GML2OGRGeometry_AddToCompositeCurve( |
2546 | 0 | poCC.get(), std::move(poGeom), |
2547 | 0 | bChildrenAreAllLineString)) |
2548 | 0 | { |
2549 | 0 | return nullptr; |
2550 | 0 | } |
2551 | 0 | } |
2552 | 0 | } |
2553 | 0 | else if (psChild->eType == CXT_Element && |
2554 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "curveMembers")) |
2555 | 0 | { |
2556 | 0 | for (const CPLXMLNode *psChild2 = psChild->psChild; |
2557 | 0 | psChild2 != nullptr; psChild2 = psChild2->psNext) |
2558 | 0 | { |
2559 | 0 | if (psChild2->eType == CXT_Element) |
2560 | 0 | { |
2561 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2562 | 0 | psChild2, pszId, |
2563 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
2564 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2565 | 0 | if (!GML2OGRGeometry_AddToCompositeCurve( |
2566 | 0 | poCC.get(), std::move(poGeom), |
2567 | 0 | bChildrenAreAllLineString)) |
2568 | 0 | { |
2569 | 0 | return nullptr; |
2570 | 0 | } |
2571 | 0 | } |
2572 | 0 | } |
2573 | 0 | } |
2574 | 0 | } |
2575 | | |
2576 | 0 | if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString) |
2577 | 0 | { |
2578 | 0 | return std::unique_ptr<OGRLineString>( |
2579 | 0 | OGRCurve::CastToLineString(poCC.release())); |
2580 | 0 | } |
2581 | | |
2582 | 0 | return poCC; |
2583 | 0 | } |
2584 | | |
2585 | | /* -------------------------------------------------------------------- */ |
2586 | | /* Curve */ |
2587 | | /* -------------------------------------------------------------------- */ |
2588 | 0 | if (EQUAL(pszBaseGeometry, "Curve") || |
2589 | 0 | EQUAL(pszBaseGeometry, "ElevatedCurve") /* AIXM */) |
2590 | 0 | { |
2591 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "segments"); |
2592 | 0 | if (psChild == nullptr) |
2593 | 0 | { |
2594 | 0 | ReportFailure("GML3 Curve geometry lacks segments element."); |
2595 | 0 | return nullptr; |
2596 | 0 | } |
2597 | | |
2598 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2599 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
2600 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2601 | 0 | if (poGeom == nullptr || !OGR_GT_IsCurve(poGeom->getGeometryType())) |
2602 | 0 | { |
2603 | 0 | ReportFailure( |
2604 | 0 | "Curve: Got %s geometry as Member instead of segments.", |
2605 | 0 | poGeom ? poGeom->getGeometryName() : "NULL"); |
2606 | 0 | return nullptr; |
2607 | 0 | } |
2608 | | |
2609 | 0 | return poGeom; |
2610 | 0 | } |
2611 | | |
2612 | | /* -------------------------------------------------------------------- */ |
2613 | | /* segments */ |
2614 | | /* -------------------------------------------------------------------- */ |
2615 | 0 | if (EQUAL(pszBaseGeometry, "segments")) |
2616 | 0 | { |
2617 | 0 | std::unique_ptr<OGRCurve> poCurve; |
2618 | 0 | std::unique_ptr<OGRCompoundCurve> poCC; |
2619 | 0 | bool bChildrenAreAllLineString = true; |
2620 | |
|
2621 | 0 | bool bLastCurveWasApproximateArc = false; |
2622 | 0 | bool bLastCurveWasApproximateArcInvertedAxisOrder = false; |
2623 | 0 | double dfLastCurveApproximateArcRadius = 0.0; |
2624 | 0 | double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS; |
2625 | |
|
2626 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
2627 | 0 | psChild = psChild->psNext) |
2628 | | |
2629 | 0 | { |
2630 | 0 | if (psChild->eType == CXT_Element |
2631 | | // && (EQUAL(BareGMLElement(psChild->pszValue), |
2632 | | // "LineStringSegment") || |
2633 | | // EQUAL(BareGMLElement(psChild->pszValue), |
2634 | | // "GeodesicString") || |
2635 | | // EQUAL(BareGMLElement(psChild->pszValue), "Arc") || |
2636 | | // EQUAL(BareGMLElement(psChild->pszValue), "Circle")) |
2637 | 0 | ) |
2638 | 0 | { |
2639 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2640 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
2641 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2642 | 0 | if (poGeom == nullptr || |
2643 | 0 | !OGR_GT_IsCurve(poGeom->getGeometryType())) |
2644 | 0 | { |
2645 | 0 | ReportFailure("segments: Got %s geometry as Member " |
2646 | 0 | "instead of curve.", |
2647 | 0 | poGeom ? poGeom->getGeometryName() : "NULL"); |
2648 | 0 | return nullptr; |
2649 | 0 | } |
2650 | | |
2651 | | // Ad-hoc logic to handle nicely connecting ArcByCenterPoint |
2652 | | // with consecutive curves, as found in some AIXM files. |
2653 | 0 | bool bIsApproximateArc = false; |
2654 | 0 | if (strcmp(BareGMLElement(psChild->pszValue), |
2655 | 0 | "ArcByCenterPoint") == 0) |
2656 | 0 | { |
2657 | 0 | storeArcByCenterPointParameters( |
2658 | 0 | psChild, pszSRSName, bIsApproximateArc, |
2659 | 0 | dfLastCurveApproximateArcRadius, |
2660 | 0 | bLastCurveWasApproximateArcInvertedAxisOrder, |
2661 | 0 | dfSemiMajor); |
2662 | 0 | } |
2663 | |
|
2664 | 0 | if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString) |
2665 | 0 | bChildrenAreAllLineString = false; |
2666 | |
|
2667 | 0 | if (poCC == nullptr && poCurve == nullptr) |
2668 | 0 | { |
2669 | 0 | poCurve.reset(poGeom.release()->toCurve()); |
2670 | 0 | } |
2671 | 0 | else |
2672 | 0 | { |
2673 | 0 | if (poCC == nullptr) |
2674 | 0 | { |
2675 | 0 | poCC = std::make_unique<OGRCompoundCurve>(); |
2676 | 0 | if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE) |
2677 | 0 | { |
2678 | 0 | return nullptr; |
2679 | 0 | } |
2680 | 0 | poCurve.reset(); |
2681 | 0 | } |
2682 | | |
2683 | 0 | connectArcByCenterPointToOtherSegments( |
2684 | 0 | poGeom.get(), poCC.get(), bIsApproximateArc, |
2685 | 0 | bLastCurveWasApproximateArc, |
2686 | 0 | dfLastCurveApproximateArcRadius, |
2687 | 0 | bLastCurveWasApproximateArcInvertedAxisOrder, |
2688 | 0 | dfSemiMajor); |
2689 | |
|
2690 | 0 | auto poAsCurve = |
2691 | 0 | std::unique_ptr<OGRCurve>(poGeom.release()->toCurve()); |
2692 | 0 | if (poCC->addCurve(std::move(poAsCurve)) != OGRERR_NONE) |
2693 | 0 | { |
2694 | 0 | return nullptr; |
2695 | 0 | } |
2696 | 0 | } |
2697 | | |
2698 | 0 | bLastCurveWasApproximateArc = bIsApproximateArc; |
2699 | 0 | } |
2700 | 0 | } |
2701 | | |
2702 | 0 | if (poCurve != nullptr) |
2703 | 0 | return poCurve; |
2704 | 0 | if (poCC == nullptr) |
2705 | 0 | return std::make_unique<OGRLineString>(); |
2706 | | |
2707 | 0 | if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString) |
2708 | 0 | { |
2709 | 0 | return std::unique_ptr<OGRLineString>( |
2710 | 0 | OGRCurve::CastToLineString(poCC.release())); |
2711 | 0 | } |
2712 | | |
2713 | 0 | return poCC; |
2714 | 0 | } |
2715 | | |
2716 | | /* -------------------------------------------------------------------- */ |
2717 | | /* MultiGeometry */ |
2718 | | /* CAUTION: OGR < 1.8.0 produced GML with GeometryCollection, which is */ |
2719 | | /* not a valid GML 2 keyword! The right name is MultiGeometry. Let's be */ |
2720 | | /* tolerant with the non compliant files we produced. */ |
2721 | | /* -------------------------------------------------------------------- */ |
2722 | 0 | if (EQUAL(pszBaseGeometry, "MultiGeometry") || |
2723 | 0 | EQUAL(pszBaseGeometry, "GeometryCollection")) |
2724 | 0 | { |
2725 | 0 | auto poGC = std::make_unique<OGRGeometryCollection>(); |
2726 | | |
2727 | | // Collect geoms. |
2728 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
2729 | 0 | psChild = psChild->psNext) |
2730 | 0 | { |
2731 | 0 | if (psChild->eType == CXT_Element && |
2732 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "geometryMember")) |
2733 | 0 | { |
2734 | 0 | const CPLXMLNode *psGeometryChild = GetChildElement(psChild); |
2735 | |
|
2736 | 0 | if (psGeometryChild != nullptr) |
2737 | 0 | { |
2738 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2739 | 0 | psGeometryChild, pszId, |
2740 | 0 | nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1, |
2741 | 0 | nSRSDimension, pszSRSName); |
2742 | 0 | if (poGeom == nullptr) |
2743 | 0 | { |
2744 | 0 | ReportFailure( |
2745 | 0 | "GeometryCollection: Failed to get geometry " |
2746 | 0 | "in geometryMember"); |
2747 | 0 | return nullptr; |
2748 | 0 | } |
2749 | | |
2750 | 0 | poGC->addGeometry(std::move(poGeom)); |
2751 | 0 | } |
2752 | 0 | } |
2753 | 0 | else if (psChild->eType == CXT_Element && |
2754 | 0 | EQUAL(BareGMLElement(psChild->pszValue), |
2755 | 0 | "geometryMembers")) |
2756 | 0 | { |
2757 | 0 | for (const CPLXMLNode *psChild2 = psChild->psChild; |
2758 | 0 | psChild2 != nullptr; psChild2 = psChild2->psNext) |
2759 | 0 | { |
2760 | 0 | if (psChild2->eType == CXT_Element) |
2761 | 0 | { |
2762 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
2763 | 0 | psChild2, pszId, |
2764 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
2765 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
2766 | 0 | if (poGeom == nullptr) |
2767 | 0 | { |
2768 | 0 | ReportFailure( |
2769 | 0 | "GeometryCollection: Failed to get geometry " |
2770 | 0 | "in geometryMember"); |
2771 | 0 | return nullptr; |
2772 | 0 | } |
2773 | | |
2774 | 0 | poGC->addGeometry(std::move(poGeom)); |
2775 | 0 | } |
2776 | 0 | } |
2777 | 0 | } |
2778 | 0 | } |
2779 | | |
2780 | 0 | return poGC; |
2781 | 0 | } |
2782 | | |
2783 | | /* -------------------------------------------------------------------- */ |
2784 | | /* Directed Edge */ |
2785 | | /* -------------------------------------------------------------------- */ |
2786 | 0 | if (EQUAL(pszBaseGeometry, "directedEdge")) |
2787 | 0 | { |
2788 | | // Collect edge. |
2789 | 0 | const CPLXMLNode *psEdge = FindBareXMLChild(psNode, "Edge"); |
2790 | 0 | if (psEdge == nullptr) |
2791 | 0 | { |
2792 | 0 | ReportFailure("Failed to get Edge element in directedEdge"); |
2793 | 0 | return nullptr; |
2794 | 0 | } |
2795 | | |
2796 | | // TODO(schwehr): Localize vars after removing gotos. |
2797 | 0 | std::unique_ptr<OGRGeometry> poGeom; |
2798 | 0 | const CPLXMLNode *psNodeElement = nullptr; |
2799 | 0 | const CPLXMLNode *psPointProperty = nullptr; |
2800 | 0 | const CPLXMLNode *psPoint = nullptr; |
2801 | 0 | bool bNodeOrientation = true; |
2802 | 0 | std::unique_ptr<OGRPoint> poPositiveNode; |
2803 | 0 | std::unique_ptr<OGRPoint> poNegativeNode; |
2804 | |
|
2805 | 0 | const bool bEdgeOrientation = GetElementOrientation(psNode); |
2806 | |
|
2807 | 0 | if (bGetSecondaryGeometry) |
2808 | 0 | { |
2809 | 0 | const CPLXMLNode *psdirectedNode = |
2810 | 0 | FindBareXMLChild(psEdge, "directedNode"); |
2811 | 0 | if (psdirectedNode == nullptr) |
2812 | 0 | goto nonode; |
2813 | | |
2814 | 0 | bNodeOrientation = GetElementOrientation(psdirectedNode); |
2815 | |
|
2816 | 0 | psNodeElement = FindBareXMLChild(psdirectedNode, "Node"); |
2817 | 0 | if (psNodeElement == nullptr) |
2818 | 0 | goto nonode; |
2819 | | |
2820 | 0 | psPointProperty = FindBareXMLChild(psNodeElement, "pointProperty"); |
2821 | 0 | if (psPointProperty == nullptr) |
2822 | 0 | psPointProperty = |
2823 | 0 | FindBareXMLChild(psNodeElement, "connectionPointProperty"); |
2824 | 0 | if (psPointProperty == nullptr) |
2825 | 0 | goto nonode; |
2826 | | |
2827 | 0 | psPoint = FindBareXMLChild(psPointProperty, "Point"); |
2828 | 0 | if (psPoint == nullptr) |
2829 | 0 | psPoint = FindBareXMLChild(psPointProperty, "ConnectionPoint"); |
2830 | 0 | if (psPoint == nullptr) |
2831 | 0 | goto nonode; |
2832 | | |
2833 | 0 | poGeom = GML2OGRGeometry_XMLNode_Internal( |
2834 | 0 | psPoint, pszId, nPseudoBoolGetSecondaryGeometryOption, |
2835 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName, true); |
2836 | 0 | if (poGeom == nullptr || |
2837 | 0 | wkbFlatten(poGeom->getGeometryType()) != wkbPoint) |
2838 | 0 | { |
2839 | | // ReportFailure( |
2840 | | // "Got %s geometry as Member instead of POINT.", |
2841 | | // poGeom ? poGeom->getGeometryName() : "NULL" ); |
2842 | 0 | goto nonode; |
2843 | 0 | } |
2844 | | |
2845 | 0 | { |
2846 | 0 | OGRPoint *poPoint = poGeom.release()->toPoint(); |
2847 | 0 | if ((bNodeOrientation == bEdgeOrientation) != bOrientation) |
2848 | 0 | poPositiveNode.reset(poPoint); |
2849 | 0 | else |
2850 | 0 | poNegativeNode.reset(poPoint); |
2851 | 0 | } |
2852 | | |
2853 | | // Look for the other node. |
2854 | 0 | psdirectedNode = psdirectedNode->psNext; |
2855 | 0 | while (psdirectedNode != nullptr && |
2856 | 0 | !EQUAL(psdirectedNode->pszValue, "directedNode")) |
2857 | 0 | psdirectedNode = psdirectedNode->psNext; |
2858 | 0 | if (psdirectedNode == nullptr) |
2859 | 0 | goto nonode; |
2860 | | |
2861 | 0 | if (GetElementOrientation(psdirectedNode) == bNodeOrientation) |
2862 | 0 | goto nonode; |
2863 | | |
2864 | 0 | psNodeElement = FindBareXMLChild(psEdge, "Node"); |
2865 | 0 | if (psNodeElement == nullptr) |
2866 | 0 | goto nonode; |
2867 | | |
2868 | 0 | psPointProperty = FindBareXMLChild(psNodeElement, "pointProperty"); |
2869 | 0 | if (psPointProperty == nullptr) |
2870 | 0 | psPointProperty = |
2871 | 0 | FindBareXMLChild(psNodeElement, "connectionPointProperty"); |
2872 | 0 | if (psPointProperty == nullptr) |
2873 | 0 | goto nonode; |
2874 | | |
2875 | 0 | psPoint = FindBareXMLChild(psPointProperty, "Point"); |
2876 | 0 | if (psPoint == nullptr) |
2877 | 0 | psPoint = FindBareXMLChild(psPointProperty, "ConnectionPoint"); |
2878 | 0 | if (psPoint == nullptr) |
2879 | 0 | goto nonode; |
2880 | | |
2881 | 0 | poGeom = GML2OGRGeometry_XMLNode_Internal( |
2882 | 0 | psPoint, pszId, nPseudoBoolGetSecondaryGeometryOption, |
2883 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName, true); |
2884 | 0 | if (poGeom == nullptr || |
2885 | 0 | wkbFlatten(poGeom->getGeometryType()) != wkbPoint) |
2886 | 0 | { |
2887 | | // ReportFailure( |
2888 | | // "Got %s geometry as Member instead of POINT.", |
2889 | | // poGeom ? poGeom->getGeometryName() : "NULL" ); |
2890 | 0 | goto nonode; |
2891 | 0 | } |
2892 | | |
2893 | 0 | { |
2894 | 0 | OGRPoint *poPoint = poGeom.release()->toPoint(); |
2895 | 0 | if ((bNodeOrientation == bEdgeOrientation) != bOrientation) |
2896 | 0 | poNegativeNode.reset(poPoint); |
2897 | 0 | else |
2898 | 0 | poPositiveNode.reset(poPoint); |
2899 | 0 | } |
2900 | |
|
2901 | 0 | { |
2902 | | // Create a scope so that poMP can be initialized with goto |
2903 | | // above and label below. |
2904 | 0 | auto poMP = std::make_unique<OGRMultiPoint>(); |
2905 | 0 | poMP->addGeometry(std::move(poNegativeNode)); |
2906 | 0 | poMP->addGeometry(std::move(poPositiveNode)); |
2907 | |
|
2908 | 0 | return poMP; |
2909 | 0 | } |
2910 | 0 | nonode:; |
2911 | 0 | } |
2912 | | |
2913 | | // Collect curveproperty. |
2914 | 0 | const CPLXMLNode *psCurveProperty = |
2915 | 0 | FindBareXMLChild(psEdge, "curveProperty"); |
2916 | 0 | if (psCurveProperty == nullptr) |
2917 | 0 | { |
2918 | 0 | ReportFailure("directedEdge: Failed to get curveProperty in Edge"); |
2919 | 0 | return nullptr; |
2920 | 0 | } |
2921 | | |
2922 | 0 | const CPLXMLNode *psCurve = |
2923 | 0 | FindBareXMLChild(psCurveProperty, "LineString"); |
2924 | 0 | if (psCurve == nullptr) |
2925 | 0 | psCurve = FindBareXMLChild(psCurveProperty, "Curve"); |
2926 | 0 | if (psCurve == nullptr) |
2927 | 0 | { |
2928 | 0 | ReportFailure("directedEdge: Failed to get LineString or " |
2929 | 0 | "Curve tag in curveProperty"); |
2930 | 0 | return nullptr; |
2931 | 0 | } |
2932 | | |
2933 | 0 | auto poLineStringBeforeCast = GML2OGRGeometry_XMLNode_Internal( |
2934 | 0 | psCurve, pszId, nPseudoBoolGetSecondaryGeometryOption, |
2935 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName, true); |
2936 | 0 | if (poLineStringBeforeCast == nullptr || |
2937 | 0 | wkbFlatten(poLineStringBeforeCast->getGeometryType()) != |
2938 | 0 | wkbLineString) |
2939 | 0 | { |
2940 | 0 | ReportFailure("Got %s geometry as Member instead of LINESTRING.", |
2941 | 0 | poLineStringBeforeCast |
2942 | 0 | ? poLineStringBeforeCast->getGeometryName() |
2943 | 0 | : "NULL"); |
2944 | 0 | return nullptr; |
2945 | 0 | } |
2946 | 0 | auto poLineString = std::unique_ptr<OGRLineString>( |
2947 | 0 | poLineStringBeforeCast.release()->toLineString()); |
2948 | |
|
2949 | 0 | if (bGetSecondaryGeometry) |
2950 | 0 | { |
2951 | | // Choose a point based on the orientation. |
2952 | 0 | poNegativeNode = std::make_unique<OGRPoint>(); |
2953 | 0 | poPositiveNode = std::make_unique<OGRPoint>(); |
2954 | 0 | if (bEdgeOrientation == bOrientation) |
2955 | 0 | { |
2956 | 0 | poLineString->StartPoint(poNegativeNode.get()); |
2957 | 0 | poLineString->EndPoint(poPositiveNode.get()); |
2958 | 0 | } |
2959 | 0 | else |
2960 | 0 | { |
2961 | 0 | poLineString->StartPoint(poPositiveNode.get()); |
2962 | 0 | poLineString->EndPoint(poNegativeNode.get()); |
2963 | 0 | } |
2964 | |
|
2965 | 0 | auto poMP = std::make_unique<OGRMultiPoint>(); |
2966 | 0 | poMP->addGeometry(std::move(poNegativeNode)); |
2967 | 0 | poMP->addGeometry(std::move(poPositiveNode)); |
2968 | 0 | return poMP; |
2969 | 0 | } |
2970 | | |
2971 | | // correct orientation of the line string |
2972 | 0 | if (bEdgeOrientation != bOrientation) |
2973 | 0 | { |
2974 | 0 | poLineString->reversePoints(); |
2975 | 0 | } |
2976 | 0 | return poLineString; |
2977 | 0 | } |
2978 | | |
2979 | | /* -------------------------------------------------------------------- */ |
2980 | | /* TopoCurve */ |
2981 | | /* -------------------------------------------------------------------- */ |
2982 | 0 | if (EQUAL(pszBaseGeometry, "TopoCurve")) |
2983 | 0 | { |
2984 | 0 | std::unique_ptr<OGRMultiLineString> poMLS; |
2985 | 0 | std::unique_ptr<OGRMultiPoint> poMP; |
2986 | |
|
2987 | 0 | if (bGetSecondaryGeometry) |
2988 | 0 | poMP = std::make_unique<OGRMultiPoint>(); |
2989 | 0 | else |
2990 | 0 | poMLS = std::make_unique<OGRMultiLineString>(); |
2991 | | |
2992 | | // Collect directedEdges. |
2993 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
2994 | 0 | psChild = psChild->psNext) |
2995 | 0 | { |
2996 | 0 | if (psChild->eType == CXT_Element && |
2997 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "directedEdge")) |
2998 | 0 | { |
2999 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
3000 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
3001 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3002 | 0 | if (poGeom == nullptr) |
3003 | 0 | { |
3004 | 0 | ReportFailure("Failed to get geometry in directedEdge"); |
3005 | 0 | return nullptr; |
3006 | 0 | } |
3007 | | |
3008 | | // Add the two points corresponding to the two nodes to poMP. |
3009 | 0 | if (bGetSecondaryGeometry && |
3010 | 0 | wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) |
3011 | 0 | { |
3012 | 0 | auto poMultiPoint = std::unique_ptr<OGRMultiPoint>( |
3013 | 0 | poGeom.release()->toMultiPoint()); |
3014 | | |
3015 | | // TODO: TopoCurve geometries with more than one |
3016 | | // directedEdge elements were not tested. |
3017 | 0 | if (poMP->getNumGeometries() <= 0 || |
3018 | 0 | !(poMP->getGeometryRef(poMP->getNumGeometries() - 1) |
3019 | 0 | ->Equals(poMultiPoint->getGeometryRef(0)))) |
3020 | 0 | { |
3021 | 0 | poMP->addGeometry(poMultiPoint->getGeometryRef(0)); |
3022 | 0 | } |
3023 | 0 | poMP->addGeometry(poMultiPoint->getGeometryRef(1)); |
3024 | 0 | } |
3025 | 0 | else if (!bGetSecondaryGeometry && |
3026 | 0 | wkbFlatten(poGeom->getGeometryType()) == wkbLineString) |
3027 | 0 | { |
3028 | 0 | poMLS->addGeometry(std::move(poGeom)); |
3029 | 0 | } |
3030 | 0 | else |
3031 | 0 | { |
3032 | 0 | ReportFailure("Got %s geometry as Member instead of %s.", |
3033 | 0 | poGeom->getGeometryName(), |
3034 | 0 | bGetSecondaryGeometry ? "MULTIPOINT" |
3035 | 0 | : "LINESTRING"); |
3036 | 0 | return nullptr; |
3037 | 0 | } |
3038 | 0 | } |
3039 | 0 | } |
3040 | | |
3041 | 0 | if (bGetSecondaryGeometry) |
3042 | 0 | return poMP; |
3043 | | |
3044 | 0 | return poMLS; |
3045 | 0 | } |
3046 | | |
3047 | | /* -------------------------------------------------------------------- */ |
3048 | | /* TopoSurface */ |
3049 | | /* -------------------------------------------------------------------- */ |
3050 | 0 | if (EQUAL(pszBaseGeometry, "TopoSurface")) |
3051 | 0 | { |
3052 | | /****************************************************************/ |
3053 | | /* applying the FaceHoleNegative = false rules */ |
3054 | | /* */ |
3055 | | /* - each <TopoSurface> is expected to represent a MultiPolygon */ |
3056 | | /* - each <Face> is expected to represent a distinct Polygon, */ |
3057 | | /* this including any possible Interior Ring (holes); */ |
3058 | | /* orientation="+/-" plays no role at all to identify "holes" */ |
3059 | | /* - each <Edge> within a <Face> may indifferently represent */ |
3060 | | /* an element of the Exterior or Interior Boundary; relative */ |
3061 | | /* order of <Edges> is absolutely irrelevant. */ |
3062 | | /****************************************************************/ |
3063 | | /* Contributor: Alessandro Furieri, a.furieri@lqt.it */ |
3064 | | /* Developed for Faunalia (http://www.faunalia.it) */ |
3065 | | /* with funding from Regione Toscana - */ |
3066 | | /* Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE */ |
3067 | | /****************************************************************/ |
3068 | 0 | if (!bFaceHoleNegative) |
3069 | 0 | { |
3070 | 0 | if (bGetSecondaryGeometry) |
3071 | 0 | return nullptr; |
3072 | | |
3073 | 0 | #ifndef HAVE_GEOS |
3074 | 0 | static bool bWarningAlreadyEmitted = false; |
3075 | 0 | if (!bWarningAlreadyEmitted) |
3076 | 0 | { |
3077 | 0 | ReportFailure( |
3078 | 0 | "Interpreating that GML TopoSurface geometry requires GDAL " |
3079 | 0 | "to be built with GEOS support. As a workaround, you can " |
3080 | 0 | "try defining the GML_FACE_HOLE_NEGATIVE configuration " |
3081 | 0 | "option to YES, so that the 'old' interpretation algorithm " |
3082 | 0 | "is used. But be warned that the result might be " |
3083 | 0 | "incorrect."); |
3084 | 0 | bWarningAlreadyEmitted = true; |
3085 | 0 | } |
3086 | 0 | return nullptr; |
3087 | | #else |
3088 | | auto poTS = std::make_unique<OGRMultiPolygon>(); |
3089 | | |
3090 | | // Collect directed faces. |
3091 | | for (const CPLXMLNode *psChild = psNode->psChild; |
3092 | | psChild != nullptr; psChild = psChild->psNext) |
3093 | | { |
3094 | | if (psChild->eType == CXT_Element && |
3095 | | EQUAL(BareGMLElement(psChild->pszValue), "directedFace")) |
3096 | | { |
3097 | | // Collect next face (psChild->psChild). |
3098 | | const CPLXMLNode *psFaceChild = GetChildElement(psChild); |
3099 | | |
3100 | | while ( |
3101 | | psFaceChild != nullptr && |
3102 | | !(psFaceChild->eType == CXT_Element && |
3103 | | EQUAL(BareGMLElement(psFaceChild->pszValue), "Face"))) |
3104 | | psFaceChild = psFaceChild->psNext; |
3105 | | |
3106 | | if (psFaceChild == nullptr) |
3107 | | continue; |
3108 | | |
3109 | | auto poCollectedGeom = |
3110 | | std::make_unique<OGRMultiLineString>(); |
3111 | | |
3112 | | // Collect directed edges of the face. |
3113 | | for (const CPLXMLNode *psDirectedEdgeChild = |
3114 | | psFaceChild->psChild; |
3115 | | psDirectedEdgeChild != nullptr; |
3116 | | psDirectedEdgeChild = psDirectedEdgeChild->psNext) |
3117 | | { |
3118 | | if (psDirectedEdgeChild->eType == CXT_Element && |
3119 | | EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue), |
3120 | | "directedEdge")) |
3121 | | { |
3122 | | auto poEdgeGeom = GML2OGRGeometry_XMLNode_Internal( |
3123 | | psDirectedEdgeChild, pszId, |
3124 | | nPseudoBoolGetSecondaryGeometryOption, |
3125 | | nRecLevel + 1, nSRSDimension, pszSRSName, true); |
3126 | | |
3127 | | if (poEdgeGeom == nullptr || |
3128 | | wkbFlatten(poEdgeGeom->getGeometryType()) != |
3129 | | wkbLineString) |
3130 | | { |
3131 | | ReportFailure( |
3132 | | "Failed to get geometry in directedEdge"); |
3133 | | return nullptr; |
3134 | | } |
3135 | | |
3136 | | poCollectedGeom->addGeometry(std::move(poEdgeGeom)); |
3137 | | } |
3138 | | } |
3139 | | |
3140 | | auto poFaceCollectionGeom = std::unique_ptr<OGRGeometry>( |
3141 | | poCollectedGeom->Polygonize()); |
3142 | | if (poFaceCollectionGeom == nullptr) |
3143 | | { |
3144 | | ReportFailure("Failed to assemble Edges in Face"); |
3145 | | return nullptr; |
3146 | | } |
3147 | | |
3148 | | auto poFaceGeom = |
3149 | | GML2FaceExtRing(poFaceCollectionGeom.get()); |
3150 | | |
3151 | | if (poFaceGeom == nullptr) |
3152 | | { |
3153 | | ReportFailure("Failed to build Polygon for Face"); |
3154 | | return nullptr; |
3155 | | } |
3156 | | else |
3157 | | { |
3158 | | int iCount = poTS->getNumGeometries(); |
3159 | | if (iCount == 0) |
3160 | | { |
3161 | | // Inserting the first Polygon. |
3162 | | poTS->addGeometry(std::move(poFaceGeom)); |
3163 | | } |
3164 | | else |
3165 | | { |
3166 | | // Using Union to add the current Polygon. |
3167 | | auto poUnion = std::unique_ptr<OGRGeometry>( |
3168 | | poTS->Union(poFaceGeom.get())); |
3169 | | if (poUnion == nullptr) |
3170 | | { |
3171 | | ReportFailure("Failed Union for TopoSurface"); |
3172 | | return nullptr; |
3173 | | } |
3174 | | if (wkbFlatten(poUnion->getGeometryType()) == |
3175 | | wkbPolygon) |
3176 | | { |
3177 | | // Forcing to be a MultiPolygon. |
3178 | | poTS = std::make_unique<OGRMultiPolygon>(); |
3179 | | poTS->addGeometry(std::move(poUnion)); |
3180 | | } |
3181 | | else if (wkbFlatten(poUnion->getGeometryType()) == |
3182 | | wkbMultiPolygon) |
3183 | | { |
3184 | | poTS.reset(poUnion.release()->toMultiPolygon()); |
3185 | | } |
3186 | | else |
3187 | | { |
3188 | | ReportFailure( |
3189 | | "Unexpected geometry type resulting " |
3190 | | "from Union for TopoSurface"); |
3191 | | return nullptr; |
3192 | | } |
3193 | | } |
3194 | | } |
3195 | | } |
3196 | | } |
3197 | | |
3198 | | return poTS; |
3199 | | #endif // HAVE_GEOS |
3200 | 0 | } |
3201 | | |
3202 | | /****************************************************************/ |
3203 | | /* applying the FaceHoleNegative = true rules */ |
3204 | | /* */ |
3205 | | /* - each <TopoSurface> is expected to represent a MultiPolygon */ |
3206 | | /* - any <Face> declaring orientation="+" is expected to */ |
3207 | | /* represent an Exterior Ring (no holes are allowed) */ |
3208 | | /* - any <Face> declaring orientation="-" is expected to */ |
3209 | | /* represent an Interior Ring (hole) belonging to the latest */ |
3210 | | /* Exterior Ring. */ |
3211 | | /* - <Edges> within the same <Face> are expected to be */ |
3212 | | /* arranged in geometrically adjacent and consecutive */ |
3213 | | /* sequence. */ |
3214 | | /****************************************************************/ |
3215 | 0 | if (bGetSecondaryGeometry) |
3216 | 0 | return nullptr; |
3217 | 0 | bool bFaceOrientation = true; |
3218 | 0 | auto poTS = std::make_unique<OGRPolygon>(); |
3219 | | |
3220 | | // Collect directed faces. |
3221 | 0 | for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr; |
3222 | 0 | psChild = psChild->psNext) |
3223 | 0 | { |
3224 | 0 | if (psChild->eType == CXT_Element && |
3225 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "directedFace")) |
3226 | 0 | { |
3227 | 0 | bFaceOrientation = GetElementOrientation(psChild); |
3228 | | |
3229 | | // Collect next face (psChild->psChild). |
3230 | 0 | const CPLXMLNode *psFaceChild = GetChildElement(psChild); |
3231 | 0 | while (psFaceChild != nullptr && |
3232 | 0 | !EQUAL(BareGMLElement(psFaceChild->pszValue), "Face")) |
3233 | 0 | psFaceChild = psFaceChild->psNext; |
3234 | |
|
3235 | 0 | if (psFaceChild == nullptr) |
3236 | 0 | continue; |
3237 | | |
3238 | 0 | auto poFaceGeom = std::make_unique<OGRLinearRing>(); |
3239 | | |
3240 | | // Collect directed edges of the face. |
3241 | 0 | for (const CPLXMLNode *psDirectedEdgeChild = |
3242 | 0 | psFaceChild->psChild; |
3243 | 0 | psDirectedEdgeChild != nullptr; |
3244 | 0 | psDirectedEdgeChild = psDirectedEdgeChild->psNext) |
3245 | 0 | { |
3246 | 0 | if (psDirectedEdgeChild->eType == CXT_Element && |
3247 | 0 | EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue), |
3248 | 0 | "directedEdge")) |
3249 | 0 | { |
3250 | 0 | auto poEdgeGeom = GML2OGRGeometry_XMLNode_Internal( |
3251 | 0 | psDirectedEdgeChild, pszId, |
3252 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
3253 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName, true, |
3254 | 0 | bFaceOrientation); |
3255 | |
|
3256 | 0 | if (poEdgeGeom == nullptr || |
3257 | 0 | wkbFlatten(poEdgeGeom->getGeometryType()) != |
3258 | 0 | wkbLineString) |
3259 | 0 | { |
3260 | 0 | ReportFailure( |
3261 | 0 | "Failed to get geometry in directedEdge"); |
3262 | 0 | return nullptr; |
3263 | 0 | } |
3264 | | |
3265 | 0 | auto poEdgeGeomLS = std::unique_ptr<OGRLineString>( |
3266 | 0 | poEdgeGeom.release()->toLineString()); |
3267 | 0 | if (!bFaceOrientation) |
3268 | 0 | { |
3269 | 0 | OGRLineString *poLS = poEdgeGeomLS.get(); |
3270 | 0 | OGRLineString *poAddLS = poFaceGeom.get(); |
3271 | | |
3272 | | // TODO(schwehr): Use AlmostEqual. |
3273 | 0 | const double epsilon = 1.0e-14; |
3274 | 0 | if (poAddLS->getNumPoints() < 2) |
3275 | 0 | { |
3276 | | // Skip it. |
3277 | 0 | } |
3278 | 0 | else if (poLS->getNumPoints() > 0 && |
3279 | 0 | fabs(poLS->getX(poLS->getNumPoints() - 1) - |
3280 | 0 | poAddLS->getX(0)) < epsilon && |
3281 | 0 | fabs(poLS->getY(poLS->getNumPoints() - 1) - |
3282 | 0 | poAddLS->getY(0)) < epsilon && |
3283 | 0 | fabs(poLS->getZ(poLS->getNumPoints() - 1) - |
3284 | 0 | poAddLS->getZ(0)) < epsilon) |
3285 | 0 | { |
3286 | | // Skip the first point of the new linestring to |
3287 | | // avoid invalidate duplicate points. |
3288 | 0 | poLS->addSubLineString(poAddLS, 1); |
3289 | 0 | } |
3290 | 0 | else |
3291 | 0 | { |
3292 | | // Add the whole new line string. |
3293 | 0 | poLS->addSubLineString(poAddLS); |
3294 | 0 | } |
3295 | 0 | poFaceGeom->empty(); |
3296 | 0 | } |
3297 | | // TODO(schwehr): Suspicious that poLS overwritten |
3298 | | // without else. |
3299 | 0 | OGRLineString *poLS = poFaceGeom.get(); |
3300 | 0 | OGRLineString *poAddLS = poEdgeGeomLS.get(); |
3301 | 0 | if (poAddLS->getNumPoints() < 2) |
3302 | 0 | { |
3303 | | // Skip it. |
3304 | 0 | } |
3305 | 0 | else if (poLS->getNumPoints() > 0 && |
3306 | 0 | fabs(poLS->getX(poLS->getNumPoints() - 1) - |
3307 | 0 | poAddLS->getX(0)) < 1e-14 && |
3308 | 0 | fabs(poLS->getY(poLS->getNumPoints() - 1) - |
3309 | 0 | poAddLS->getY(0)) < 1e-14 && |
3310 | 0 | fabs(poLS->getZ(poLS->getNumPoints() - 1) - |
3311 | 0 | poAddLS->getZ(0)) < 1e-14) |
3312 | 0 | { |
3313 | | // Skip the first point of the new linestring to |
3314 | | // avoid invalidate duplicate points. |
3315 | 0 | poLS->addSubLineString(poAddLS, 1); |
3316 | 0 | } |
3317 | 0 | else |
3318 | 0 | { |
3319 | | // Add the whole new line string. |
3320 | 0 | poLS->addSubLineString(poAddLS); |
3321 | 0 | } |
3322 | 0 | } |
3323 | 0 | } |
3324 | | |
3325 | | // if( poFaceGeom == NULL ) |
3326 | | // { |
3327 | | // ReportFailure( |
3328 | | // "Failed to get Face geometry in directedFace" |
3329 | | // ); |
3330 | | // delete poFaceGeom; |
3331 | | // return NULL; |
3332 | | // } |
3333 | | |
3334 | 0 | poTS->addRing(std::move(poFaceGeom)); |
3335 | 0 | } |
3336 | 0 | } |
3337 | | |
3338 | | // if( poTS == NULL ) |
3339 | | // { |
3340 | | // ReportFailure( |
3341 | | // "Failed to get TopoSurface geometry" ); |
3342 | | // delete poTS; |
3343 | | // return NULL; |
3344 | | // } |
3345 | | |
3346 | 0 | return poTS; |
3347 | 0 | } |
3348 | | |
3349 | | /* -------------------------------------------------------------------- */ |
3350 | | /* Surface */ |
3351 | | /* -------------------------------------------------------------------- */ |
3352 | 0 | if (EQUAL(pszBaseGeometry, "Surface") || |
3353 | 0 | EQUAL(pszBaseGeometry, "ElevatedSurface") /* AIXM */) |
3354 | 0 | { |
3355 | | // Find outer ring. |
3356 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "patches"); |
3357 | 0 | if (psChild == nullptr) |
3358 | 0 | psChild = FindBareXMLChild(psNode, "polygonPatches"); |
3359 | 0 | if (psChild == nullptr) |
3360 | 0 | psChild = FindBareXMLChild(psNode, "trianglePatches"); |
3361 | |
|
3362 | 0 | psChild = GetChildElement(psChild); |
3363 | 0 | if (psChild == nullptr) |
3364 | 0 | { |
3365 | | // <gml:Surface/> and <gml:Surface><gml:patches/></gml:Surface> are |
3366 | | // valid GML. |
3367 | 0 | return std::make_unique<OGRPolygon>(); |
3368 | 0 | } |
3369 | | |
3370 | 0 | OGRMultiSurface *poMSPtr = nullptr; |
3371 | 0 | std::unique_ptr<OGRGeometry> poResultPoly; |
3372 | 0 | std::unique_ptr<OGRGeometry> poResultTri; |
3373 | 0 | OGRTriangulatedSurface *poTINPtr = nullptr; |
3374 | 0 | for (; psChild != nullptr; psChild = psChild->psNext) |
3375 | 0 | { |
3376 | 0 | if (psChild->eType == CXT_Element && |
3377 | 0 | (EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch") || |
3378 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "Rectangle"))) |
3379 | 0 | { |
3380 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
3381 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
3382 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3383 | 0 | if (poGeom == nullptr) |
3384 | 0 | { |
3385 | 0 | return nullptr; |
3386 | 0 | } |
3387 | | |
3388 | 0 | const OGRwkbGeometryType eGeomType = |
3389 | 0 | wkbFlatten(poGeom->getGeometryType()); |
3390 | |
|
3391 | 0 | if (poResultPoly == nullptr) |
3392 | 0 | poResultPoly = std::move(poGeom); |
3393 | 0 | else |
3394 | 0 | { |
3395 | 0 | if (poMSPtr == nullptr) |
3396 | 0 | { |
3397 | 0 | std::unique_ptr<OGRMultiSurface> poMS; |
3398 | 0 | if (wkbFlatten(poResultPoly->getGeometryType()) == |
3399 | 0 | wkbPolygon && |
3400 | 0 | eGeomType == wkbPolygon) |
3401 | 0 | poMS = std::make_unique<OGRMultiPolygon>(); |
3402 | 0 | else |
3403 | 0 | poMS = std::make_unique<OGRMultiSurface>(); |
3404 | 0 | OGRErr eErr = |
3405 | 0 | poMS->addGeometry(std::move(poResultPoly)); |
3406 | 0 | CPL_IGNORE_RET_VAL(eErr); |
3407 | 0 | CPLAssert(eErr == OGRERR_NONE); |
3408 | 0 | poResultPoly = std::move(poMS); |
3409 | 0 | poMSPtr = cpl::down_cast<OGRMultiSurface *>( |
3410 | 0 | poResultPoly.get()); |
3411 | 0 | } |
3412 | 0 | else if (eGeomType != wkbPolygon && |
3413 | 0 | wkbFlatten(poResultPoly->getGeometryType()) == |
3414 | 0 | wkbMultiPolygon) |
3415 | 0 | { |
3416 | 0 | OGRMultiPolygon *poMultiPoly = |
3417 | 0 | poResultPoly.release()->toMultiPolygon(); |
3418 | 0 | poResultPoly.reset( |
3419 | 0 | OGRMultiPolygon::CastToMultiSurface(poMultiPoly)); |
3420 | 0 | poMSPtr = cpl::down_cast<OGRMultiSurface *>( |
3421 | 0 | poResultPoly.get()); |
3422 | 0 | } |
3423 | 0 | OGRErr eErr = poMSPtr->addGeometry(std::move(poGeom)); |
3424 | 0 | CPL_IGNORE_RET_VAL(eErr); |
3425 | 0 | CPLAssert(eErr == OGRERR_NONE); |
3426 | 0 | } |
3427 | 0 | } |
3428 | 0 | else if (psChild->eType == CXT_Element && |
3429 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "Triangle")) |
3430 | 0 | { |
3431 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
3432 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
3433 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3434 | 0 | if (poGeom == nullptr) |
3435 | 0 | { |
3436 | 0 | return nullptr; |
3437 | 0 | } |
3438 | | |
3439 | 0 | if (poResultTri == nullptr) |
3440 | 0 | poResultTri = std::move(poGeom); |
3441 | 0 | else |
3442 | 0 | { |
3443 | 0 | if (poTINPtr == nullptr) |
3444 | 0 | { |
3445 | 0 | auto poTIN = std::make_unique<OGRTriangulatedSurface>(); |
3446 | 0 | OGRErr eErr = |
3447 | 0 | poTIN->addGeometry(std::move(poResultTri)); |
3448 | 0 | CPL_IGNORE_RET_VAL(eErr); |
3449 | 0 | CPLAssert(eErr == OGRERR_NONE); |
3450 | 0 | poResultTri = std::move(poTIN); |
3451 | 0 | poTINPtr = cpl::down_cast<OGRTriangulatedSurface *>( |
3452 | 0 | poResultTri.get()); |
3453 | 0 | } |
3454 | 0 | OGRErr eErr = poTINPtr->addGeometry(std::move(poGeom)); |
3455 | 0 | CPL_IGNORE_RET_VAL(eErr); |
3456 | 0 | CPLAssert(eErr == OGRERR_NONE); |
3457 | 0 | } |
3458 | 0 | } |
3459 | 0 | } |
3460 | | |
3461 | 0 | if (poResultTri == nullptr && poResultPoly == nullptr) |
3462 | 0 | return nullptr; |
3463 | | |
3464 | 0 | if (poResultTri == nullptr) |
3465 | 0 | return poResultPoly; |
3466 | 0 | else if (poResultPoly == nullptr) |
3467 | 0 | return poResultTri; |
3468 | 0 | else |
3469 | 0 | { |
3470 | 0 | auto poGC = std::make_unique<OGRGeometryCollection>(); |
3471 | 0 | poGC->addGeometry(std::move(poResultTri)); |
3472 | 0 | poGC->addGeometry(std::move(poResultPoly)); |
3473 | 0 | return poGC; |
3474 | 0 | } |
3475 | 0 | } |
3476 | | |
3477 | | /* -------------------------------------------------------------------- */ |
3478 | | /* TriangulatedSurface */ |
3479 | | /* -------------------------------------------------------------------- */ |
3480 | 0 | if (EQUAL(pszBaseGeometry, "TriangulatedSurface") || |
3481 | 0 | EQUAL(pszBaseGeometry, "Tin")) |
3482 | 0 | { |
3483 | | // Find trianglePatches. |
3484 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "trianglePatches"); |
3485 | 0 | if (psChild == nullptr) |
3486 | 0 | psChild = FindBareXMLChild(psNode, "patches"); |
3487 | |
|
3488 | 0 | psChild = GetChildElement(psChild); |
3489 | 0 | if (psChild == nullptr) |
3490 | 0 | { |
3491 | 0 | ReportFailure("Missing <trianglePatches> for %s.", pszBaseGeometry); |
3492 | 0 | return nullptr; |
3493 | 0 | } |
3494 | | |
3495 | 0 | auto poTIN = std::make_unique<OGRTriangulatedSurface>(); |
3496 | 0 | for (; psChild != nullptr; psChild = psChild->psNext) |
3497 | 0 | { |
3498 | 0 | if (psChild->eType == CXT_Element && |
3499 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "Triangle")) |
3500 | 0 | { |
3501 | 0 | auto poTriangle = GML2OGRGeometry_XMLNode_Internal( |
3502 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
3503 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3504 | 0 | if (poTriangle == nullptr) |
3505 | 0 | { |
3506 | 0 | return nullptr; |
3507 | 0 | } |
3508 | 0 | else |
3509 | 0 | { |
3510 | 0 | poTIN->addGeometry(std::move(poTriangle)); |
3511 | 0 | } |
3512 | 0 | } |
3513 | 0 | } |
3514 | | |
3515 | 0 | return poTIN; |
3516 | 0 | } |
3517 | | |
3518 | | /* -------------------------------------------------------------------- */ |
3519 | | /* PolyhedralSurface */ |
3520 | | /* -------------------------------------------------------------------- */ |
3521 | 0 | if (EQUAL(pszBaseGeometry, "PolyhedralSurface")) |
3522 | 0 | { |
3523 | | // Find polygonPatches. |
3524 | 0 | const CPLXMLNode *psParent = FindBareXMLChild(psNode, "polygonPatches"); |
3525 | 0 | if (psParent == nullptr) |
3526 | 0 | { |
3527 | 0 | if (GetChildElement(psNode) == nullptr) |
3528 | 0 | { |
3529 | | // This is empty PolyhedralSurface. |
3530 | 0 | return std::make_unique<OGRPolyhedralSurface>(); |
3531 | 0 | } |
3532 | 0 | else |
3533 | 0 | { |
3534 | 0 | ReportFailure("Missing <polygonPatches> for %s.", |
3535 | 0 | pszBaseGeometry); |
3536 | 0 | return nullptr; |
3537 | 0 | } |
3538 | 0 | } |
3539 | | |
3540 | 0 | const CPLXMLNode *psChild = GetChildElement(psParent); |
3541 | 0 | if (psChild == nullptr) |
3542 | 0 | { |
3543 | | // This is empty PolyhedralSurface. |
3544 | 0 | return std::make_unique<OGRPolyhedralSurface>(); |
3545 | 0 | } |
3546 | 0 | else if (!EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch")) |
3547 | 0 | { |
3548 | 0 | ReportFailure("Missing <PolygonPatch> for %s.", pszBaseGeometry); |
3549 | 0 | return nullptr; |
3550 | 0 | } |
3551 | | |
3552 | | // Each psParent has the tags corresponding to <gml:polygonPatches> |
3553 | | // Each psChild has the tags corresponding to <gml:PolygonPatch> |
3554 | | // Each PolygonPatch has a set of polygons enclosed in a |
3555 | | // OGRPolyhedralSurface. |
3556 | 0 | auto poGC = std::make_unique<OGRGeometryCollection>(); |
3557 | 0 | for (; psParent != nullptr; psParent = psParent->psNext) |
3558 | 0 | { |
3559 | 0 | psChild = GetChildElement(psParent); |
3560 | 0 | if (psChild == nullptr) |
3561 | 0 | continue; |
3562 | 0 | auto poPS = std::make_unique<OGRPolyhedralSurface>(); |
3563 | 0 | for (; psChild != nullptr; psChild = psChild->psNext) |
3564 | 0 | { |
3565 | 0 | if (psChild->eType == CXT_Element && |
3566 | 0 | EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch")) |
3567 | 0 | { |
3568 | 0 | auto poPolygon = GML2OGRGeometry_XMLNode_Internal( |
3569 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
3570 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3571 | 0 | if (poPolygon == nullptr) |
3572 | 0 | { |
3573 | 0 | ReportFailure("Wrong geometry type for %s.", |
3574 | 0 | pszBaseGeometry); |
3575 | 0 | return nullptr; |
3576 | 0 | } |
3577 | | |
3578 | 0 | else if (wkbFlatten(poPolygon->getGeometryType()) == |
3579 | 0 | wkbPolygon) |
3580 | 0 | { |
3581 | 0 | poPS->addGeometry(std::move(poPolygon)); |
3582 | 0 | } |
3583 | 0 | else if (wkbFlatten(poPolygon->getGeometryType()) == |
3584 | 0 | wkbCurvePolygon) |
3585 | 0 | { |
3586 | 0 | poPS->addGeometryDirectly( |
3587 | 0 | OGRGeometryFactory::forceToPolygon( |
3588 | 0 | poPolygon.release())); |
3589 | 0 | } |
3590 | 0 | else |
3591 | 0 | { |
3592 | 0 | ReportFailure("Wrong geometry type for %s.", |
3593 | 0 | pszBaseGeometry); |
3594 | 0 | return nullptr; |
3595 | 0 | } |
3596 | 0 | } |
3597 | 0 | } |
3598 | 0 | poGC->addGeometry(std::move(poPS)); |
3599 | 0 | } |
3600 | | |
3601 | 0 | if (poGC->getNumGeometries() == 0) |
3602 | 0 | { |
3603 | 0 | return nullptr; |
3604 | 0 | } |
3605 | 0 | else if (poGC->getNumGeometries() == 1) |
3606 | 0 | { |
3607 | 0 | auto poResult = |
3608 | 0 | std::unique_ptr<OGRGeometry>(poGC->getGeometryRef(0)); |
3609 | 0 | poGC->removeGeometry(0, FALSE); |
3610 | 0 | return poResult; |
3611 | 0 | } |
3612 | 0 | else |
3613 | 0 | { |
3614 | 0 | return poGC; |
3615 | 0 | } |
3616 | 0 | } |
3617 | | |
3618 | | /* -------------------------------------------------------------------- */ |
3619 | | /* Solid */ |
3620 | | /* -------------------------------------------------------------------- */ |
3621 | 0 | if (EQUAL(pszBaseGeometry, "Solid")) |
3622 | 0 | { |
3623 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "interior"); |
3624 | 0 | if (psChild != nullptr) |
3625 | 0 | { |
3626 | 0 | static bool bWarnedOnce = false; |
3627 | 0 | if (!bWarnedOnce) |
3628 | 0 | { |
3629 | 0 | ReportWarning("<interior> elements of <Solid> are ignored"); |
3630 | 0 | bWarnedOnce = true; |
3631 | 0 | } |
3632 | 0 | } |
3633 | | |
3634 | | // Find exterior element. |
3635 | 0 | psChild = FindBareXMLChild(psNode, "exterior"); |
3636 | |
|
3637 | 0 | if (nSRSDimension == 0) |
3638 | 0 | nSRSDimension = 3; |
3639 | |
|
3640 | 0 | psChild = GetChildElement(psChild); |
3641 | 0 | if (psChild == nullptr) |
3642 | 0 | { |
3643 | | // <gml:Solid/> and <gml:Solid><gml:exterior/></gml:Solid> are valid |
3644 | | // GML. |
3645 | 0 | return std::make_unique<OGRPolyhedralSurface>(); |
3646 | 0 | } |
3647 | | |
3648 | 0 | if (EQUAL(BareGMLElement(psChild->pszValue), "CompositeSurface")) |
3649 | 0 | { |
3650 | 0 | auto poPS = std::make_unique<OGRPolyhedralSurface>(); |
3651 | | |
3652 | | // Iterate over children. |
3653 | 0 | for (psChild = psChild->psChild; psChild != nullptr; |
3654 | 0 | psChild = psChild->psNext) |
3655 | 0 | { |
3656 | 0 | const char *pszMemberElement = |
3657 | 0 | BareGMLElement(psChild->pszValue); |
3658 | 0 | if (psChild->eType == CXT_Element && |
3659 | 0 | (EQUAL(pszMemberElement, "polygonMember") || |
3660 | 0 | EQUAL(pszMemberElement, "surfaceMember"))) |
3661 | 0 | { |
3662 | 0 | const CPLXMLNode *psSurfaceChild = GetChildElement(psChild); |
3663 | |
|
3664 | 0 | if (psSurfaceChild != nullptr) |
3665 | 0 | { |
3666 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
3667 | 0 | psSurfaceChild, pszId, |
3668 | 0 | nPseudoBoolGetSecondaryGeometryOption, |
3669 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3670 | 0 | if (poGeom != nullptr && |
3671 | 0 | wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) |
3672 | 0 | { |
3673 | 0 | poPS->addGeometry(std::move(poGeom)); |
3674 | 0 | } |
3675 | 0 | } |
3676 | 0 | } |
3677 | 0 | } |
3678 | 0 | return poPS; |
3679 | 0 | } |
3680 | | |
3681 | | // Get the geometry inside <exterior>. |
3682 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
3683 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
3684 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3685 | 0 | if (poGeom == nullptr) |
3686 | 0 | { |
3687 | 0 | ReportFailure("Invalid exterior element"); |
3688 | 0 | return nullptr; |
3689 | 0 | } |
3690 | | |
3691 | 0 | return poGeom; |
3692 | 0 | } |
3693 | | |
3694 | | /* -------------------------------------------------------------------- */ |
3695 | | /* OrientableCurve */ |
3696 | | /* -------------------------------------------------------------------- */ |
3697 | 0 | if (EQUAL(pszBaseGeometry, "OrientableCurve")) |
3698 | 0 | { |
3699 | | // Find baseCurve. |
3700 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "baseCurve"); |
3701 | |
|
3702 | 0 | psChild = GetChildElement(psChild); |
3703 | 0 | if (psChild == nullptr) |
3704 | 0 | { |
3705 | 0 | ReportFailure("Missing <baseCurve> for OrientableCurve."); |
3706 | 0 | return nullptr; |
3707 | 0 | } |
3708 | | |
3709 | 0 | auto poGeom = GML2OGRGeometry_XMLNode_Internal( |
3710 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
3711 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3712 | 0 | if (!poGeom || !OGR_GT_IsCurve(poGeom->getGeometryType())) |
3713 | 0 | { |
3714 | 0 | ReportFailure("baseCurve of OrientableCurve is not a curve."); |
3715 | 0 | return nullptr; |
3716 | 0 | } |
3717 | 0 | if (!GetElementOrientation(psNode)) |
3718 | 0 | { |
3719 | 0 | poGeom->toCurve()->reversePoints(); |
3720 | 0 | } |
3721 | 0 | return poGeom; |
3722 | 0 | } |
3723 | | |
3724 | | /* -------------------------------------------------------------------- */ |
3725 | | /* OrientableSurface */ |
3726 | | /* -------------------------------------------------------------------- */ |
3727 | 0 | if (EQUAL(pszBaseGeometry, "OrientableSurface")) |
3728 | 0 | { |
3729 | | // Find baseSurface. |
3730 | 0 | const CPLXMLNode *psChild = FindBareXMLChild(psNode, "baseSurface"); |
3731 | |
|
3732 | 0 | psChild = GetChildElement(psChild); |
3733 | 0 | if (psChild == nullptr) |
3734 | 0 | { |
3735 | 0 | ReportFailure("Missing <baseSurface> for OrientableSurface."); |
3736 | 0 | return nullptr; |
3737 | 0 | } |
3738 | | |
3739 | 0 | return GML2OGRGeometry_XMLNode_Internal( |
3740 | 0 | psChild, pszId, nPseudoBoolGetSecondaryGeometryOption, |
3741 | 0 | nRecLevel + 1, nSRSDimension, pszSRSName); |
3742 | 0 | } |
3743 | | |
3744 | | /* -------------------------------------------------------------------- */ |
3745 | | /* SimplePolygon, SimpleRectangle, SimpleTriangle */ |
3746 | | /* (GML 3.3 compact encoding) */ |
3747 | | /* -------------------------------------------------------------------- */ |
3748 | 0 | if (EQUAL(pszBaseGeometry, "SimplePolygon") || |
3749 | 0 | EQUAL(pszBaseGeometry, "SimpleRectangle")) |
3750 | 0 | { |
3751 | 0 | auto poRing = std::make_unique<OGRLinearRing>(); |
3752 | |
|
3753 | 0 | if (!ParseGMLCoordinates(psNode, poRing.get(), nSRSDimension)) |
3754 | 0 | { |
3755 | 0 | return nullptr; |
3756 | 0 | } |
3757 | | |
3758 | 0 | poRing->closeRings(); |
3759 | |
|
3760 | 0 | auto poPolygon = std::make_unique<OGRPolygon>(); |
3761 | 0 | poPolygon->addRing(std::move(poRing)); |
3762 | 0 | return poPolygon; |
3763 | 0 | } |
3764 | | |
3765 | 0 | if (EQUAL(pszBaseGeometry, "SimpleTriangle")) |
3766 | 0 | { |
3767 | 0 | auto poRing = std::make_unique<OGRLinearRing>(); |
3768 | |
|
3769 | 0 | if (!ParseGMLCoordinates(psNode, poRing.get(), nSRSDimension)) |
3770 | 0 | { |
3771 | 0 | return nullptr; |
3772 | 0 | } |
3773 | | |
3774 | 0 | poRing->closeRings(); |
3775 | |
|
3776 | 0 | auto poTriangle = std::make_unique<OGRTriangle>(); |
3777 | 0 | poTriangle->addRing(std::move(poRing)); |
3778 | 0 | return poTriangle; |
3779 | 0 | } |
3780 | | |
3781 | | /* -------------------------------------------------------------------- */ |
3782 | | /* SimpleMultiPoint (GML 3.3 compact encoding) */ |
3783 | | /* -------------------------------------------------------------------- */ |
3784 | 0 | if (EQUAL(pszBaseGeometry, "SimpleMultiPoint")) |
3785 | 0 | { |
3786 | 0 | auto poLS = std::make_unique<OGRLineString>(); |
3787 | |
|
3788 | 0 | if (!ParseGMLCoordinates(psNode, poLS.get(), nSRSDimension)) |
3789 | 0 | { |
3790 | 0 | return nullptr; |
3791 | 0 | } |
3792 | | |
3793 | 0 | auto poMP = std::make_unique<OGRMultiPoint>(); |
3794 | 0 | int nPoints = poLS->getNumPoints(); |
3795 | 0 | for (int i = 0; i < nPoints; i++) |
3796 | 0 | { |
3797 | 0 | auto poPoint = std::make_unique<OGRPoint>(); |
3798 | 0 | poLS->getPoint(i, poPoint.get()); |
3799 | 0 | poMP->addGeometry(std::move(poPoint)); |
3800 | 0 | } |
3801 | 0 | return poMP; |
3802 | 0 | } |
3803 | | |
3804 | 0 | if (strcmp(pszBaseGeometry, "null") == 0) |
3805 | 0 | { |
3806 | 0 | return nullptr; |
3807 | 0 | } |
3808 | | |
3809 | 0 | ReportFailure("Unrecognized geometry type <%s>.", pszBaseGeometry); |
3810 | |
|
3811 | 0 | return nullptr; |
3812 | 0 | } |
3813 | | |
3814 | | /************************************************************************/ |
3815 | | /* OGR_G_CreateFromGMLTree() */ |
3816 | | /************************************************************************/ |
3817 | | |
3818 | | /** Create geometry from GML */ |
3819 | | OGRGeometryH OGR_G_CreateFromGMLTree(const CPLXMLNode *psTree) |
3820 | | |
3821 | 0 | { |
3822 | 0 | return OGRGeometry::ToHandle(GML2OGRGeometry_XMLNode(psTree, -1)); |
3823 | 0 | } |
3824 | | |
3825 | | /************************************************************************/ |
3826 | | /* OGR_G_CreateFromGML() */ |
3827 | | /************************************************************************/ |
3828 | | |
3829 | | /** |
3830 | | * \brief Create geometry from GML. |
3831 | | * |
3832 | | * This method translates a fragment of GML containing only the geometry |
3833 | | * portion into a corresponding OGRGeometry. There are many limitations |
3834 | | * on the forms of GML geometries supported by this parser, but they are |
3835 | | * too numerous to list here. |
3836 | | * |
3837 | | * The following GML2 elements are parsed : Point, LineString, Polygon, |
3838 | | * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry. |
3839 | | * |
3840 | | * The following GML3 elements are parsed : Surface, |
3841 | | * MultiSurface, PolygonPatch, Triangle, Rectangle, Curve, MultiCurve, |
3842 | | * CompositeCurve, LineStringSegment, Arc, Circle, CompositeSurface, |
3843 | | * OrientableSurface, Solid, Tin, TriangulatedSurface. |
3844 | | * |
3845 | | * Arc and Circle elements are returned as curves by default. Stroking to |
3846 | | * linestrings can be done with |
3847 | | * OGR_G_ForceTo(hGeom, OGR_GT_GetLinear(OGR_G_GetGeometryType(hGeom)), NULL). |
3848 | | * A 4 degrees step is used by default, unless the user |
3849 | | * has overridden the value with the OGR_ARC_STEPSIZE configuration variable. |
3850 | | * |
3851 | | * The C++ method OGRGeometryFactory::createFromGML() is the same as |
3852 | | * this function. |
3853 | | * |
3854 | | * @param pszGML The GML fragment for the geometry. |
3855 | | * |
3856 | | * @return a geometry on success, or NULL on error. |
3857 | | * |
3858 | | * @see OGR_G_ForceTo() |
3859 | | * @see OGR_GT_GetLinear() |
3860 | | * @see OGR_G_GetGeometryType() |
3861 | | */ |
3862 | | |
3863 | | OGRGeometryH OGR_G_CreateFromGML(const char *pszGML) |
3864 | | |
3865 | 0 | { |
3866 | 0 | if (pszGML == nullptr || strlen(pszGML) == 0) |
3867 | 0 | { |
3868 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3869 | 0 | "GML Geometry is empty in OGR_G_CreateFromGML()."); |
3870 | 0 | return nullptr; |
3871 | 0 | } |
3872 | | |
3873 | | /* -------------------------------------------------------------------- */ |
3874 | | /* Try to parse the XML snippet using the MiniXML API. If this */ |
3875 | | /* fails, we assume the minixml api has already posted a CPL */ |
3876 | | /* error, and just return NULL. */ |
3877 | | /* -------------------------------------------------------------------- */ |
3878 | 0 | CPLXMLNode *psGML = CPLParseXMLString(pszGML); |
3879 | |
|
3880 | 0 | if (psGML == nullptr) |
3881 | 0 | return nullptr; |
3882 | | |
3883 | | /* -------------------------------------------------------------------- */ |
3884 | | /* Convert geometry recursively. */ |
3885 | | /* -------------------------------------------------------------------- */ |
3886 | | // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer() |
3887 | | // and GMLReader::GMLReader(). |
3888 | 0 | const bool bFaceHoleNegative = |
3889 | 0 | CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")); |
3890 | 0 | OGRGeometry *poGeometry = GML2OGRGeometry_XMLNode(psGML, -1, 0, 0, false, |
3891 | 0 | true, bFaceHoleNegative); |
3892 | |
|
3893 | 0 | CPLDestroyXMLNode(psGML); |
3894 | |
|
3895 | 0 | return OGRGeometry::ToHandle(poGeometry); |
3896 | 0 | } |