Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GML Reader
4 : * Purpose: Implementation of GMLReader class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, Frank Warmerdam
9 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "gmlreaderp.h"
16 : #include "gmlreader.h"
17 :
18 : #include <climits>
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <algorithm>
22 : #include <set>
23 : #include <string>
24 :
25 : #include "cpl_conv.h"
26 : #include "cpl_error.h"
27 : #include "cpl_multiproc.h"
28 : #include "cpl_string.h"
29 : #include "cpl_vsi_virtual.h"
30 : #include "gmlutils.h"
31 : #include "ogr_geometry.h"
32 :
33 : #ifdef EMBED_RESOURCE_FILES
34 : #include "embedded_resources.h"
35 : #endif
36 :
37 : /************************************************************************/
38 : /* ~IGMLReader() */
39 : /************************************************************************/
40 :
41 401 : IGMLReader::~IGMLReader()
42 : {
43 401 : }
44 :
45 : /************************************************************************/
46 : /* ==================================================================== */
47 : /* No XERCES or EXPAT Library */
48 : /* ==================================================================== */
49 : /************************************************************************/
50 : #if !defined(HAVE_XERCES) && !defined(HAVE_EXPAT)
51 :
52 : /************************************************************************/
53 : /* CreateGMLReader() */
54 : /************************************************************************/
55 :
56 : IGMLReader *CreateGMLReader(bool /*bUseExpatParserPreferably*/,
57 : bool /*bInvertAxisOrderIfLatLong*/,
58 : bool /*bConsiderEPSGAsURN*/,
59 : GMLSwapCoordinatesEnum /* eSwapCoordinates */,
60 : bool /*bGetSecondaryGeometryOption*/)
61 : {
62 : CPLError(CE_Failure, CPLE_AppDefined,
63 : "Unable to create Xerces C++ or Expat based GML reader, Xerces "
64 : "or Expat support not configured into GDAL/OGR.");
65 : return nullptr;
66 : }
67 :
68 : /************************************************************************/
69 : /* ==================================================================== */
70 : /* With XERCES or EXPAT Library */
71 : /* ==================================================================== */
72 : /************************************************************************/
73 : #else /* defined(HAVE_XERCES) || defined(HAVE_EXPAT) */
74 :
75 : /************************************************************************/
76 : /* CreateGMLReader() */
77 : /************************************************************************/
78 :
79 396 : IGMLReader *CreateGMLReader(bool bUseExpatParserPreferably,
80 : bool bInvertAxisOrderIfLatLong,
81 : bool bConsiderEPSGAsURN,
82 : GMLSwapCoordinatesEnum eSwapCoordinates,
83 : bool bGetSecondaryGeometryOption)
84 :
85 : {
86 : return new GMLReader(bUseExpatParserPreferably, bInvertAxisOrderIfLatLong,
87 : bConsiderEPSGAsURN, eSwapCoordinates,
88 396 : bGetSecondaryGeometryOption);
89 : }
90 :
91 : #endif
92 :
93 : CPLMutex *GMLReader::hMutex = nullptr;
94 :
95 : /************************************************************************/
96 : /* GMLReader() */
97 : /************************************************************************/
98 :
99 396 : GMLReader::GMLReader(
100 : #if !defined(HAVE_EXPAT) || !defined(HAVE_XERCES)
101 : CPL_UNUSED
102 : #endif
103 : bool bUseExpatParserPreferably,
104 : bool bInvertAxisOrderIfLatLong, bool bConsiderEPSGAsURN,
105 396 : GMLSwapCoordinatesEnum eSwapCoordinates, bool bGetSecondaryGeometryOption)
106 : : // Experimental. Not publicly advertized. See commented doc in
107 : // drv_gml.html
108 : m_bFetchAllGeometries(
109 396 : CPLTestBool(CPLGetConfigOption("GML_FETCH_ALL_GEOMETRIES", "NO"))),
110 : m_bInvertAxisOrderIfLatLong(bInvertAxisOrderIfLatLong),
111 : m_bConsiderEPSGAsURN(bConsiderEPSGAsURN),
112 : m_eSwapCoordinates(eSwapCoordinates),
113 : m_bGetSecondaryGeometryOption(bGetSecondaryGeometryOption),
114 : // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer(),
115 : // and GMLReader::GMLReader().
116 : m_bFaceHoleNegative(
117 792 : CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
118 : {
119 : #ifndef HAVE_XERCES
120 : #else
121 : #ifdef HAVE_EXPAT
122 396 : if (bUseExpatParserPreferably)
123 394 : bUseExpatReader = true;
124 : #endif
125 : #endif
126 :
127 : #if defined(HAVE_EXPAT) && defined(HAVE_XERCES)
128 396 : if (bUseExpatReader)
129 394 : CPLDebug("GML", "Using Expat reader");
130 : else
131 2 : CPLDebug("GML", "Using Xerces reader");
132 : #endif
133 396 : }
134 :
135 : /************************************************************************/
136 : /* ~GMLReader() */
137 : /************************************************************************/
138 :
139 792 : GMLReader::~GMLReader()
140 :
141 : {
142 396 : GMLReader::ClearClasses();
143 :
144 396 : CPLFree(m_pszFilename);
145 :
146 396 : CleanupParser();
147 :
148 396 : delete m_poRecycledState;
149 :
150 : #ifdef HAVE_XERCES
151 396 : if (m_bXercesInitialized)
152 2 : OGRDeinitializeXerces();
153 : #endif
154 : #ifdef HAVE_EXPAT
155 396 : CPLFree(pabyBuf);
156 : #endif
157 :
158 396 : if (fpGML)
159 395 : VSIFCloseL(fpGML);
160 396 : fpGML = nullptr;
161 :
162 396 : CPLFree(m_pszGlobalSRSName);
163 :
164 396 : CPLFree(m_pszFilteredClassName);
165 792 : }
166 :
167 : /************************************************************************/
168 : /* SetSourceFile() */
169 : /************************************************************************/
170 :
171 399 : void GMLReader::SetSourceFile(const char *pszFilename)
172 :
173 : {
174 399 : CPLFree(m_pszFilename);
175 399 : m_pszFilename = CPLStrdup(pszFilename);
176 399 : }
177 :
178 : /************************************************************************/
179 : /* GetSourceFileName() */
180 : /************************************************************************/
181 :
182 395 : const char *GMLReader::GetSourceFileName()
183 : {
184 395 : return m_pszFilename;
185 : }
186 :
187 : /************************************************************************/
188 : /* SetFP() */
189 : /************************************************************************/
190 :
191 386 : void GMLReader::SetFP(VSILFILE *fp)
192 : {
193 386 : fpGML = fp;
194 386 : }
195 :
196 : /************************************************************************/
197 : /* SetupParser() */
198 : /************************************************************************/
199 :
200 519 : bool GMLReader::SetupParser()
201 :
202 : {
203 519 : if (fpGML == nullptr)
204 12 : fpGML = VSIFOpenL(m_pszFilename, "rt");
205 519 : if (fpGML != nullptr)
206 519 : VSIFSeekL(fpGML, 0, SEEK_SET);
207 :
208 519 : int bRet = -1;
209 : #ifdef HAVE_EXPAT
210 519 : if (bUseExpatReader)
211 517 : bRet = SetupParserExpat();
212 : #endif
213 :
214 : #ifdef HAVE_XERCES
215 519 : if (!bUseExpatReader)
216 2 : bRet = SetupParserXerces();
217 : #endif
218 519 : if (bRet < 0)
219 : {
220 0 : CPLError(CE_Failure, CPLE_AppDefined,
221 : "SetupParser(): should not happen");
222 0 : return false;
223 : }
224 :
225 519 : if (!bRet)
226 0 : return false;
227 :
228 519 : m_bReadStarted = false;
229 :
230 : // Push an empty state.
231 519 : PushState(m_poRecycledState ? m_poRecycledState : new GMLReadState());
232 519 : m_poRecycledState = nullptr;
233 :
234 519 : return true;
235 : }
236 :
237 : #ifdef HAVE_XERCES
238 : /************************************************************************/
239 : /* SetupParserXerces() */
240 : /************************************************************************/
241 :
242 2 : bool GMLReader::SetupParserXerces()
243 : {
244 2 : if (!m_bXercesInitialized)
245 : {
246 2 : if (!OGRInitializeXerces())
247 0 : return false;
248 2 : m_bXercesInitialized = true;
249 : }
250 :
251 : // Cleanup any old parser.
252 2 : if (m_poSAXReader != nullptr)
253 0 : CleanupParser();
254 :
255 : // Create and initialize parser.
256 2 : XMLCh *xmlUriValid = nullptr;
257 2 : XMLCh *xmlUriNS = nullptr;
258 :
259 : try
260 : {
261 2 : m_poSAXReader = XMLReaderFactory::createXMLReader();
262 :
263 2 : GMLXercesHandler *poXercesHandler = new GMLXercesHandler(this);
264 2 : m_poGMLHandler = poXercesHandler;
265 :
266 2 : m_poSAXReader->setContentHandler(poXercesHandler);
267 2 : m_poSAXReader->setErrorHandler(poXercesHandler);
268 2 : m_poSAXReader->setLexicalHandler(poXercesHandler);
269 2 : m_poSAXReader->setEntityResolver(poXercesHandler);
270 2 : m_poSAXReader->setDTDHandler(poXercesHandler);
271 2 : m_poSAXReader->setFeature(
272 2 : XMLUni::fgXercesDisableDefaultEntityResolution, true);
273 :
274 2 : xmlUriValid =
275 2 : XMLString::transcode("http://xml.org/sax/features/validation");
276 2 : xmlUriNS =
277 2 : XMLString::transcode("http://xml.org/sax/features/namespaces");
278 :
279 : #if (OGR_GML_VALIDATION)
280 : m_poSAXReader->setFeature(xmlUriValid, true);
281 : m_poSAXReader->setFeature(xmlUriNS, true);
282 :
283 : m_poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
284 : m_poSAXReader->setFeature(XMLUni::fgXercesSchema, true);
285 :
286 : // m_poSAXReader->setDoSchema(true);
287 : // m_poSAXReader->setValidationSchemaFullChecking(true);
288 : #else
289 2 : m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, false);
290 :
291 2 : m_poSAXReader->setFeature(XMLUni::fgXercesSchema, false);
292 :
293 : #endif
294 2 : XMLString::release(&xmlUriValid);
295 2 : XMLString::release(&xmlUriNS);
296 : }
297 0 : catch (...)
298 : {
299 0 : XMLString::release(&xmlUriValid);
300 0 : XMLString::release(&xmlUriNS);
301 :
302 0 : CPLError(CE_Warning, CPLE_AppDefined,
303 : "Exception initializing Xerces based GML reader.\n");
304 0 : return false;
305 : }
306 :
307 2 : if (m_GMLInputSource == nullptr && fpGML != nullptr)
308 2 : m_GMLInputSource = OGRCreateXercesInputSource(fpGML);
309 :
310 2 : return true;
311 : }
312 : #endif
313 :
314 : /************************************************************************/
315 : /* SetupParserExpat() */
316 : /************************************************************************/
317 :
318 : #ifdef HAVE_EXPAT
319 517 : bool GMLReader::SetupParserExpat()
320 : {
321 : // Cleanup any old parser.
322 517 : if (oParser != nullptr)
323 3 : CleanupParser();
324 :
325 517 : oParser = OGRCreateExpatXMLParser();
326 517 : m_poGMLHandler = new GMLExpatHandler(this, oParser);
327 :
328 517 : XML_SetElementHandler(oParser, GMLExpatHandler::startElementCbk,
329 : GMLExpatHandler::endElementCbk);
330 517 : XML_SetCharacterDataHandler(oParser, GMLExpatHandler::dataHandlerCbk);
331 517 : XML_SetUserData(oParser, m_poGMLHandler);
332 :
333 517 : if (pabyBuf == nullptr)
334 308 : pabyBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(PARSER_BUF_SIZE));
335 517 : if (pabyBuf == nullptr)
336 0 : return false;
337 :
338 517 : return true;
339 : }
340 : #endif
341 :
342 : /************************************************************************/
343 : /* CleanupParser() */
344 : /************************************************************************/
345 :
346 1213 : void GMLReader::CleanupParser()
347 :
348 : {
349 : #ifdef HAVE_XERCES
350 1213 : if (!bUseExpatReader && m_poSAXReader == nullptr)
351 3 : return;
352 : #endif
353 :
354 : #ifdef HAVE_EXPAT
355 1210 : if (bUseExpatReader && oParser == nullptr)
356 691 : return;
357 : #endif
358 :
359 1042 : while (m_poState)
360 523 : PopState();
361 :
362 : #ifdef HAVE_XERCES
363 519 : delete m_poSAXReader;
364 519 : m_poSAXReader = nullptr;
365 519 : OGRDestroyXercesInputSource(m_GMLInputSource);
366 519 : m_GMLInputSource = nullptr;
367 519 : delete m_poCompleteFeature;
368 519 : m_poCompleteFeature = nullptr;
369 519 : m_bEOF = false;
370 : #endif
371 :
372 : #ifdef HAVE_EXPAT
373 519 : if (oParser)
374 517 : XML_ParserFree(oParser);
375 519 : oParser = nullptr;
376 :
377 1049 : for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
378 530 : delete ppoFeatureTab[i];
379 519 : CPLFree(ppoFeatureTab);
380 519 : nFeatureTabIndex = 0;
381 519 : nFeatureTabLength = 0;
382 519 : nFeatureTabAlloc = 0;
383 519 : ppoFeatureTab = nullptr;
384 519 : m_osErrorMessage.clear();
385 :
386 : #endif
387 :
388 519 : delete m_poGMLHandler;
389 519 : m_poGMLHandler = nullptr;
390 :
391 519 : m_bReadStarted = false;
392 : }
393 :
394 : /************************************************************************/
395 : /* NextFeatureXerces() */
396 : /************************************************************************/
397 :
398 : #ifdef HAVE_XERCES
399 2 : GMLFeature *GMLReader::NextFeatureXerces()
400 :
401 : {
402 2 : GMLFeature *poReturn = nullptr;
403 :
404 2 : if (m_bEOF)
405 0 : return nullptr;
406 :
407 : try
408 : {
409 2 : if (!m_bReadStarted)
410 : {
411 2 : if (m_poSAXReader == nullptr)
412 2 : SetupParser();
413 :
414 2 : m_bReadStarted = true;
415 :
416 2 : if (m_poSAXReader == nullptr || m_GMLInputSource == nullptr)
417 0 : return nullptr;
418 :
419 2 : if (!m_poSAXReader->parseFirst(*m_GMLInputSource, m_oToFill))
420 0 : return nullptr;
421 : }
422 :
423 942 : while (m_poCompleteFeature == nullptr && !m_bStopParsing &&
424 940 : m_poSAXReader->parseNext(m_oToFill))
425 : {
426 : }
427 :
428 1 : if (m_poCompleteFeature == nullptr)
429 0 : m_bEOF = true;
430 :
431 1 : poReturn = m_poCompleteFeature;
432 1 : m_poCompleteFeature = nullptr;
433 : }
434 0 : catch (const XMLException &toCatch)
435 : {
436 0 : CPLString osErrMsg;
437 0 : transcode(toCatch.getMessage(), osErrMsg);
438 0 : CPLDebug("GML", "Error during NextFeature()! Message:\n%s",
439 : osErrMsg.c_str());
440 0 : m_bStopParsing = true;
441 : }
442 2 : catch (const SAXException &toCatch)
443 : {
444 1 : CPLString osErrMsg;
445 1 : transcode(toCatch.getMessage(), osErrMsg);
446 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrMsg.c_str());
447 1 : m_bStopParsing = true;
448 : }
449 :
450 2 : return poReturn;
451 : }
452 : #endif
453 :
454 : #ifdef HAVE_EXPAT
455 2183 : GMLFeature *GMLReader::NextFeatureExpat()
456 :
457 : {
458 2183 : if (!m_bReadStarted)
459 : {
460 517 : if (oParser == nullptr)
461 408 : SetupParser();
462 :
463 517 : m_bReadStarted = true;
464 : }
465 :
466 2183 : if (nFeatureTabIndex < nFeatureTabLength)
467 : {
468 1473 : return ppoFeatureTab[nFeatureTabIndex++];
469 : }
470 :
471 710 : if (!m_osErrorMessage.empty())
472 : {
473 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", m_osErrorMessage.c_str());
474 2 : m_osErrorMessage.clear();
475 2 : return nullptr;
476 : }
477 :
478 1225 : if (fpGML == nullptr || m_bStopParsing || VSIFEofL(fpGML) ||
479 517 : VSIFErrorL(fpGML))
480 191 : return nullptr;
481 :
482 517 : nFeatureTabLength = 0;
483 517 : nFeatureTabIndex = 0;
484 :
485 517 : int nDone = 0;
486 0 : do
487 : {
488 : // Reset counter that is used to detect billion laugh attacks.
489 517 : static_cast<GMLExpatHandler *>(m_poGMLHandler)
490 517 : ->ResetDataHandlerCounter();
491 :
492 : unsigned int nLen = static_cast<unsigned int>(
493 517 : VSIFReadL(pabyBuf, 1, PARSER_BUF_SIZE, fpGML));
494 517 : nDone = nLen < PARSER_BUF_SIZE;
495 :
496 : // Some files, such as APT_AIXM.xml from
497 : // https://nfdc.faa.gov/webContent/56DaySub/2015-03-05/aixm5.1.zip
498 : // end with trailing nul characters. This test is not fully bullet-proof
499 : // in case the nul characters would occur at a buffer boundary.
500 517 : while (nDone && nLen > 0 && pabyBuf[nLen - 1] == '\0')
501 0 : nLen--;
502 :
503 517 : if (XML_Parse(oParser, pabyBuf, nLen, nDone) == XML_STATUS_ERROR)
504 : {
505 : // Defer emission of the error message until we have to return
506 : // nullptr
507 : m_osErrorMessage.Printf(
508 : "XML parsing of GML file failed : %s "
509 : "at line %d, column %d",
510 : XML_ErrorString(XML_GetErrorCode(oParser)),
511 10 : static_cast<int>(XML_GetCurrentLineNumber(oParser)),
512 5 : static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
513 5 : m_bStopParsing = true;
514 : }
515 517 : if (!m_bStopParsing)
516 512 : m_bStopParsing = static_cast<GMLExpatHandler *>(m_poGMLHandler)
517 512 : ->HasStoppedParsing();
518 517 : } while (!nDone && !m_bStopParsing && nFeatureTabLength == 0);
519 :
520 517 : if (nFeatureTabLength)
521 473 : return ppoFeatureTab[nFeatureTabIndex++];
522 :
523 44 : if (!m_osErrorMessage.empty())
524 : {
525 3 : CPLError(CE_Failure, CPLE_AppDefined, "%s", m_osErrorMessage.c_str());
526 3 : m_osErrorMessage.clear();
527 : }
528 :
529 44 : return nullptr;
530 : }
531 : #endif
532 :
533 2185 : GMLFeature *GMLReader::NextFeature()
534 : {
535 : #ifdef HAVE_EXPAT
536 2185 : if (bUseExpatReader)
537 2183 : return NextFeatureExpat();
538 : #ifdef HAVE_XERCES
539 2 : return NextFeatureXerces();
540 : #else
541 : CPLError(CE_Failure, CPLE_AppDefined, "NextFeature(): Should not happen");
542 : return nullptr;
543 : #endif
544 : #else
545 : #ifdef HAVE_XERCES
546 : if (!bUseExpatReader)
547 : return NextFeatureXerces();
548 : #endif
549 : CPLError(CE_Failure, CPLE_AppDefined, "NextFeature(): Should not happen");
550 : return nullptr;
551 : #endif
552 : }
553 :
554 : /************************************************************************/
555 : /* PushFeature() */
556 : /* */
557 : /* Create a feature based on the named element. If the */
558 : /* corresponding feature class doesn't exist yet, then create */
559 : /* it now. A new GMLReadState will be created for the feature, */
560 : /* and it will be placed within that state. The state is */
561 : /* pushed onto the readstate stack. */
562 : /************************************************************************/
563 :
564 2477 : void GMLReader::PushFeature(const char *pszElement, const char *pszFID,
565 : int nClassIndex)
566 :
567 : {
568 2477 : int iClass = 0;
569 :
570 2477 : if (nClassIndex != INT_MAX)
571 : {
572 1131 : iClass = nClassIndex;
573 : }
574 : else
575 : {
576 : /* --------------------------------------------------------------------
577 : */
578 : /* Find the class of this element. */
579 : /* --------------------------------------------------------------------
580 : */
581 3575 : for (; iClass < m_nClassCount; iClass++)
582 : {
583 3458 : if (EQUAL(pszElement, m_papoClass[iClass]->GetElementName()))
584 1229 : break;
585 : }
586 :
587 : /* --------------------------------------------------------------------
588 : */
589 : /* Create a new feature class for this element, if there is no */
590 : /* existing class for it. */
591 : /* --------------------------------------------------------------------
592 : */
593 1346 : if (iClass == m_nClassCount)
594 : {
595 117 : CPLAssert(!m_bClassListLocked);
596 :
597 117 : GMLFeatureClass *poNewClass = new GMLFeatureClass(pszElement);
598 :
599 117 : AddClass(poNewClass);
600 : }
601 : }
602 :
603 : /* -------------------------------------------------------------------- */
604 : /* Create a feature of this feature class. Try to set the fid */
605 : /* if available. */
606 : /* -------------------------------------------------------------------- */
607 2477 : GMLFeature *poFeature = new GMLFeature(m_papoClass[iClass]);
608 2477 : if (pszFID != nullptr)
609 : {
610 1453 : poFeature->SetFID(pszFID);
611 : }
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Create and push a new read state. */
615 : /* -------------------------------------------------------------------- */
616 : GMLReadState *poState =
617 2477 : m_poRecycledState ? m_poRecycledState : new GMLReadState();
618 2477 : m_poRecycledState = nullptr;
619 2477 : poState->m_poFeature = poFeature;
620 2477 : PushState(poState);
621 2477 : }
622 :
623 : /************************************************************************/
624 : /* IsFeatureElement() */
625 : /* */
626 : /* Based on context and the element name, is this element a new */
627 : /* GML feature element? */
628 : /************************************************************************/
629 :
630 5178 : int GMLReader::GetFeatureElementIndex(const char *pszElement,
631 : int nElementLength,
632 : GMLAppSchemaType eAppSchemaType)
633 :
634 : {
635 5178 : const char *pszLast = m_poState->GetLastComponent();
636 5178 : const size_t nLenLast = m_poState->GetLastComponentLen();
637 :
638 5178 : if (eAppSchemaType == APPSCHEMA_MTKGML)
639 : {
640 44 : if (m_poState->m_nPathLength != 1)
641 22 : return -1;
642 : }
643 5134 : else if ((nLenLast >= 6 && EQUAL(pszLast + nLenLast - 6, "member")) ||
644 1447 : (nLenLast >= 7 && EQUAL(pszLast + nLenLast - 7, "members")))
645 : {
646 : // Default feature name.
647 : }
648 : else
649 : {
650 2521 : if (nLenLast == 4 && strcmp(pszLast, "dane") == 0)
651 : {
652 : // Polish TBD GML.
653 : }
654 :
655 : // Begin of OpenLS.
656 2521 : else if (nLenLast == 19 && nElementLength == 15 &&
657 1 : strcmp(pszLast, "GeocodeResponseList") == 0 &&
658 0 : strcmp(pszElement, "GeocodedAddress") == 0)
659 : {
660 : }
661 2521 : else if (nLenLast == 22 &&
662 3 : strcmp(pszLast, "DetermineRouteResponse") == 0)
663 : {
664 : // We don't want the children of RouteInstructionsList
665 : // to be a single feature. We want each RouteInstruction
666 : // to be a feature.
667 3 : if (strcmp(pszElement, "RouteInstructionsList") == 0)
668 1 : return -1;
669 : }
670 2518 : else if (nElementLength == 16 && nLenLast == 21 &&
671 9 : strcmp(pszElement, "RouteInstruction") == 0 &&
672 9 : strcmp(pszLast, "RouteInstructionsList") == 0)
673 : {
674 : }
675 : // End of OpenLS.
676 :
677 2509 : else if (nLenLast > 6 &&
678 513 : strcmp(pszLast + nLenLast - 6, "_layer") == 0 &&
679 0 : nElementLength > 8 &&
680 0 : strcmp(pszElement + nElementLength - 8, "_feature") == 0)
681 : {
682 : // GML answer of MapServer WMS GetFeatureInfo request.
683 : }
684 :
685 : // Begin of CSW SearchResults.
686 2509 : else if (nElementLength == static_cast<int>(strlen("BriefRecord")) &&
687 4 : nLenLast == strlen("SearchResults") &&
688 4 : strcmp(pszElement, "BriefRecord") == 0 &&
689 4 : strcmp(pszLast, "SearchResults") == 0)
690 : {
691 : }
692 2505 : else if (nElementLength == static_cast<int>(strlen("SummaryRecord")) &&
693 4 : nLenLast == strlen("SearchResults") &&
694 4 : strcmp(pszElement, "SummaryRecord") == 0 &&
695 4 : strcmp(pszLast, "SearchResults") == 0)
696 : {
697 : }
698 2501 : else if (nElementLength == static_cast<int>(strlen("Record")) &&
699 45 : nLenLast == strlen("SearchResults") &&
700 45 : strcmp(pszElement, "Record") == 0 &&
701 44 : strcmp(pszLast, "SearchResults") == 0)
702 : {
703 : }
704 : /* End of CSW SearchResults */
705 :
706 : else
707 : {
708 2457 : if (m_bClassListLocked)
709 : {
710 12241 : for (int i = 0; i < m_nClassCount; i++)
711 : {
712 10770 : if (m_poState->osPath.size() + 1 + nElementLength ==
713 10770 : m_papoClass[i]->GetElementNameLen() &&
714 158 : m_papoClass[i]
715 158 : ->GetElementName()[m_poState->osPath.size()] ==
716 103 : '|' &&
717 103 : memcmp(m_poState->osPath.c_str(),
718 103 : m_papoClass[i]->GetElementName(),
719 11031 : m_poState->osPath.size()) == 0 &&
720 206 : memcmp(pszElement,
721 206 : m_papoClass[i]->GetElementName() + 1 +
722 103 : m_poState->osPath.size(),
723 : nElementLength) == 0)
724 : {
725 81 : return i;
726 : }
727 : }
728 : // Give a chance to find a feature class by element name
729 : // This is for example needed for
730 : // autotest/ogr/data/gml_jpfgd/BldA.xml that has a
731 : // feature at a low nesting level.
732 : }
733 : else
734 : {
735 905 : return -1;
736 : }
737 : }
738 : }
739 :
740 : // If the class list isn't locked, any element that is a featureMember
741 : // will do.
742 4169 : if (!m_bClassListLocked)
743 1346 : return INT_MAX;
744 :
745 : // otherwise, find a class with the desired element name.
746 16912 : for (int i = 0; i < m_nClassCount; i++)
747 : {
748 30870 : if (nElementLength ==
749 17683 : static_cast<int>(m_papoClass[i]->GetElementNameLen()) &&
750 2248 : memcmp(pszElement, m_papoClass[i]->GetElementName(),
751 : nElementLength) == 0)
752 1346 : return i;
753 : }
754 :
755 1477 : return -1;
756 : }
757 :
758 : /************************************************************************/
759 : /* IsCityGMLGenericAttributeElement() */
760 : /************************************************************************/
761 :
762 132 : bool GMLReader::IsCityGMLGenericAttributeElement(const char *pszElement,
763 : void *attr)
764 :
765 : {
766 132 : if (strcmp(pszElement, "stringAttribute") != 0 &&
767 94 : strcmp(pszElement, "intAttribute") != 0 &&
768 92 : strcmp(pszElement, "doubleAttribute") != 0)
769 90 : return false;
770 :
771 42 : char *pszVal = m_poGMLHandler->GetAttributeValue(attr, "name");
772 42 : if (pszVal == nullptr)
773 0 : return false;
774 :
775 42 : GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
776 :
777 : // If the schema is not yet locked, then any simple element
778 : // is potentially an attribute.
779 42 : if (!poClass->IsSchemaLocked())
780 : {
781 42 : CPLFree(pszVal);
782 42 : return true;
783 : }
784 :
785 0 : for (int i = 0; i < poClass->GetPropertyCount(); i++)
786 : {
787 0 : if (strcmp(poClass->GetProperty(i)->GetSrcElement(), pszVal) == 0)
788 : {
789 0 : CPLFree(pszVal);
790 0 : return true;
791 : }
792 : }
793 :
794 0 : CPLFree(pszVal);
795 0 : return false;
796 : }
797 :
798 : /************************************************************************/
799 : /* GetAttributeElementIndex() */
800 : /************************************************************************/
801 :
802 11075 : int GMLReader::GetAttributeElementIndex(const char *pszElement, int nLen,
803 : const char *pszAttrKey)
804 :
805 : {
806 11075 : GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
807 :
808 : // If the schema is not yet locked, then any simple element
809 : // is potentially an attribute.
810 11075 : if (!poClass->IsSchemaLocked())
811 5883 : return INT_MAX;
812 :
813 : // Otherwise build the path to this element into a single string
814 : // and compare against known attributes.
815 5192 : if (m_poState->m_nPathLength == 0)
816 : {
817 3334 : if (pszAttrKey == nullptr)
818 3261 : return poClass->GetPropertyIndexBySrcElement(pszElement, nLen);
819 : else
820 : {
821 73 : int nFullLen = nLen + 1 + static_cast<int>(strlen(pszAttrKey));
822 73 : osElemPath.reserve(nFullLen);
823 73 : osElemPath.assign(pszElement, nLen);
824 73 : osElemPath.append(1, '@');
825 73 : osElemPath.append(pszAttrKey);
826 73 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
827 73 : nFullLen);
828 : }
829 : }
830 : else
831 : {
832 1858 : int nFullLen = nLen + static_cast<int>(m_poState->osPath.size()) + 1;
833 1858 : if (pszAttrKey != nullptr)
834 89 : nFullLen += 1 + static_cast<int>(strlen(pszAttrKey));
835 1858 : osElemPath.reserve(nFullLen);
836 1858 : osElemPath.assign(m_poState->osPath);
837 1858 : osElemPath.append(1, '|');
838 1858 : osElemPath.append(pszElement, nLen);
839 1858 : if (pszAttrKey != nullptr)
840 : {
841 89 : osElemPath.append(1, '@');
842 89 : osElemPath.append(pszAttrKey);
843 : }
844 1858 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
845 1858 : nFullLen);
846 : }
847 : }
848 :
849 : /************************************************************************/
850 : /* PopState() */
851 : /************************************************************************/
852 :
853 2996 : void GMLReader::PopState()
854 :
855 : {
856 2996 : if (m_poState != nullptr)
857 : {
858 : #ifdef HAVE_XERCES
859 2996 : if (!bUseExpatReader && m_poState->m_poFeature != nullptr &&
860 1 : m_poCompleteFeature == nullptr)
861 : {
862 1 : m_poCompleteFeature = m_poState->m_poFeature;
863 1 : m_poState->m_poFeature = nullptr;
864 : }
865 2995 : else if (!bUseExpatReader && m_poState->m_poFeature != nullptr)
866 : {
867 0 : delete m_poState->m_poFeature;
868 0 : m_poState->m_poFeature = nullptr;
869 : }
870 : #endif
871 :
872 : #ifdef HAVE_EXPAT
873 2996 : if (bUseExpatReader && m_poState->m_poFeature != nullptr)
874 : {
875 2476 : if (nFeatureTabLength >= nFeatureTabAlloc)
876 : {
877 528 : nFeatureTabAlloc = nFeatureTabLength * 4 / 3 + 16;
878 528 : ppoFeatureTab = static_cast<GMLFeature **>(CPLRealloc(
879 528 : ppoFeatureTab, sizeof(GMLFeature *) * (nFeatureTabAlloc)));
880 : }
881 2476 : ppoFeatureTab[nFeatureTabLength] = m_poState->m_poFeature;
882 2476 : nFeatureTabLength++;
883 :
884 2476 : m_poState->m_poFeature = nullptr;
885 : }
886 : #endif
887 :
888 2996 : GMLReadState *poParent = m_poState->m_poParentState;
889 :
890 2996 : delete m_poRecycledState;
891 2996 : m_poRecycledState = m_poState;
892 2996 : m_poRecycledState->Reset();
893 2996 : m_poState = poParent;
894 : }
895 2996 : }
896 :
897 : /************************************************************************/
898 : /* PushState() */
899 : /************************************************************************/
900 :
901 2996 : void GMLReader::PushState(GMLReadState *poState)
902 :
903 : {
904 2996 : poState->m_poParentState = m_poState;
905 2996 : m_poState = poState;
906 2996 : }
907 :
908 : /************************************************************************/
909 : /* GetClass() */
910 : /************************************************************************/
911 :
912 842 : GMLFeatureClass *GMLReader::GetClass(int iClass) const
913 :
914 : {
915 842 : if (iClass < 0 || iClass >= m_nClassCount)
916 0 : return nullptr;
917 :
918 842 : return m_papoClass[iClass];
919 : }
920 :
921 : /************************************************************************/
922 : /* GetClass() */
923 : /************************************************************************/
924 :
925 1535 : GMLFeatureClass *GMLReader::GetClass(const char *pszName) const
926 :
927 : {
928 2666 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
929 : {
930 1666 : if (EQUAL(m_papoClass[iClass]->GetName(), pszName))
931 535 : return m_papoClass[iClass];
932 : }
933 :
934 1000 : return nullptr;
935 : }
936 :
937 : /************************************************************************/
938 : /* AddClass() */
939 : /************************************************************************/
940 :
941 634 : int GMLReader::AddClass(GMLFeatureClass *poNewClass)
942 :
943 : {
944 634 : CPLAssert(GetClass(poNewClass->GetName()) == nullptr);
945 :
946 634 : m_nClassCount++;
947 634 : m_papoClass = static_cast<GMLFeatureClass **>(
948 634 : CPLRealloc(m_papoClass, sizeof(void *) * m_nClassCount));
949 634 : m_papoClass[m_nClassCount - 1] = poNewClass;
950 :
951 634 : if (poNewClass->HasFeatureProperties())
952 8 : m_bLookForClassAtAnyLevel = true;
953 :
954 634 : return m_nClassCount - 1;
955 : }
956 :
957 : /************************************************************************/
958 : /* ClearClasses() */
959 : /************************************************************************/
960 :
961 537 : void GMLReader::ClearClasses()
962 :
963 : {
964 1166 : for (int i = 0; i < m_nClassCount; i++)
965 629 : delete m_papoClass[i];
966 537 : CPLFree(m_papoClass);
967 :
968 537 : m_nClassCount = 0;
969 537 : m_papoClass = nullptr;
970 537 : m_bLookForClassAtAnyLevel = false;
971 537 : }
972 :
973 : /************************************************************************/
974 : /* SetFeaturePropertyDirectly() */
975 : /* */
976 : /* Set the property value on the current feature, adding the */
977 : /* property name to the GMLFeatureClass if required. */
978 : /* The pszValue ownership is passed to this function. */
979 : /************************************************************************/
980 :
981 6491 : void GMLReader::SetFeaturePropertyDirectly(const char *pszElement,
982 : char *pszValue, int iPropertyIn,
983 : GMLPropertyType eType)
984 :
985 : {
986 6491 : GMLFeature *poFeature = GetState()->m_poFeature;
987 :
988 6491 : CPLAssert(poFeature != nullptr);
989 :
990 : /* -------------------------------------------------------------------- */
991 : /* Does this property exist in the feature class? If not, add */
992 : /* it. */
993 : /* -------------------------------------------------------------------- */
994 6491 : GMLFeatureClass *poClass = poFeature->GetClass();
995 6491 : int iProperty = 0;
996 :
997 6491 : const int nPropertyCount = poClass->GetPropertyCount();
998 6491 : if (iPropertyIn >= 0 && iPropertyIn < nPropertyCount)
999 : {
1000 2718 : iProperty = iPropertyIn;
1001 : }
1002 : else
1003 : {
1004 9706 : for (; iProperty < nPropertyCount; iProperty++)
1005 : {
1006 9359 : if (strcmp(poClass->GetProperty(iProperty)->GetSrcElement(),
1007 9359 : pszElement) == 0)
1008 3426 : break;
1009 : }
1010 :
1011 3773 : if (iProperty == nPropertyCount)
1012 : {
1013 347 : if (poClass->IsSchemaLocked())
1014 : {
1015 1 : CPLDebug("GML",
1016 : "Encountered property missing from class schema : %s.",
1017 : pszElement);
1018 1 : CPLFree(pszValue);
1019 1 : return;
1020 : }
1021 :
1022 346 : CPLString osFieldName;
1023 :
1024 346 : if (IsWFSJointLayer())
1025 : {
1026 : // At that point the element path should be
1027 : // member|layer|property.
1028 :
1029 : // Strip member| prefix. Should always be true normally.
1030 12 : if (STARTS_WITH(pszElement, "member|"))
1031 12 : osFieldName = pszElement + strlen("member|");
1032 :
1033 : // Replace layer|property by layer_property.
1034 12 : size_t iPos = osFieldName.find('|');
1035 12 : if (iPos != std::string::npos)
1036 8 : osFieldName[iPos] = '.';
1037 :
1038 : // Special case for gml:id on layer.
1039 12 : iPos = osFieldName.find("@id");
1040 12 : if (iPos != std::string::npos)
1041 : {
1042 4 : osFieldName.resize(iPos);
1043 4 : osFieldName += ".gml_id";
1044 : }
1045 : }
1046 334 : else if (strchr(pszElement, '|') == nullptr)
1047 : {
1048 272 : osFieldName = pszElement;
1049 : }
1050 : else
1051 : {
1052 62 : osFieldName = strrchr(pszElement, '|') + 1;
1053 62 : if (poClass->GetPropertyIndex(osFieldName) != -1)
1054 4 : osFieldName = pszElement;
1055 : }
1056 :
1057 346 : size_t nPos = osFieldName.find("@");
1058 346 : if (nPos != std::string::npos)
1059 3 : osFieldName[nPos] = '_';
1060 :
1061 : // Does this conflict with an existing property name?
1062 347 : for (int i = 0; poClass->GetProperty(osFieldName) != nullptr; i++)
1063 : {
1064 1 : osFieldName += "_";
1065 1 : if (i == 10)
1066 : {
1067 0 : CPLDebug("GML", "Too many conflicting property names : %s.",
1068 : osFieldName.c_str());
1069 0 : CPLFree(pszValue);
1070 0 : return;
1071 : }
1072 : }
1073 :
1074 : GMLPropertyDefn *poPDefn =
1075 346 : new GMLPropertyDefn(osFieldName, pszElement);
1076 :
1077 346 : if (EQUAL(CPLGetConfigOption("GML_FIELDTYPES", ""),
1078 : "ALWAYS_STRING"))
1079 0 : poPDefn->SetType(GMLPT_String);
1080 346 : else if (eType != GMLPT_Untyped)
1081 3 : poPDefn->SetType(eType);
1082 :
1083 346 : if (poClass->AddProperty(poPDefn) < 0)
1084 : {
1085 0 : delete poPDefn;
1086 0 : CPLFree(pszValue);
1087 0 : return;
1088 : }
1089 : }
1090 : }
1091 :
1092 : /* -------------------------------------------------------------------- */
1093 : /* Set the property */
1094 : /* -------------------------------------------------------------------- */
1095 6490 : poFeature->SetPropertyDirectly(iProperty, pszValue);
1096 :
1097 : /* -------------------------------------------------------------------- */
1098 : /* Do we need to update the property type? */
1099 : /* -------------------------------------------------------------------- */
1100 6490 : if (!poClass->IsSchemaLocked() && !EQUAL(pszValue, OGR_GML_NULL))
1101 : {
1102 3649 : auto poClassProperty = poClass->GetProperty(iProperty);
1103 3649 : if (poClassProperty)
1104 : {
1105 3649 : poClassProperty->AnalysePropertyValue(
1106 3649 : poFeature->GetProperty(iProperty), m_bSetWidthFlag);
1107 : }
1108 : else
1109 : {
1110 0 : CPLAssert(false);
1111 : }
1112 : }
1113 : }
1114 :
1115 : /************************************************************************/
1116 : /* LoadClasses() */
1117 : /************************************************************************/
1118 :
1119 54 : bool GMLReader::LoadClasses(const char *pszFile)
1120 :
1121 : {
1122 : // Add logic later to determine reasonable default schema file.
1123 54 : if (pszFile == nullptr)
1124 0 : return false;
1125 :
1126 : /* -------------------------------------------------------------------- */
1127 : /* Load the raw XML file. */
1128 : /* -------------------------------------------------------------------- */
1129 54 : GByte *pabyRet = nullptr;
1130 54 : const char *pszWholeText = nullptr;
1131 : {
1132 : #ifdef EMBED_RESOURCE_FILES
1133 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1134 : #endif
1135 54 : if (VSIIngestFile(nullptr, pszFile, &pabyRet, nullptr,
1136 54 : 100 * 1024 * 1024))
1137 : {
1138 54 : pszWholeText = reinterpret_cast<const char *>(pabyRet);
1139 : }
1140 : else
1141 : {
1142 : #ifdef EMBED_RESOURCE_FILES
1143 : pszWholeText = GMLGetFileContent(pszFile);
1144 : if (pszWholeText)
1145 : {
1146 : CPLDebug("GML", "Using embedded %s", pszFile);
1147 : }
1148 : #endif
1149 : }
1150 : }
1151 54 : if (!pszWholeText)
1152 0 : return false;
1153 :
1154 54 : if (strstr(pszWholeText, "<GMLFeatureClassList") == nullptr)
1155 : {
1156 0 : VSIFree(pabyRet);
1157 0 : CPLError(CE_Failure, CPLE_AppDefined,
1158 : "File %s does not contain a GMLFeatureClassList tree.",
1159 : pszFile);
1160 0 : return false;
1161 : }
1162 :
1163 : /* -------------------------------------------------------------------- */
1164 : /* Convert to XML parse tree. */
1165 : /* -------------------------------------------------------------------- */
1166 108 : CPLXMLTreeCloser psRoot(CPLParseXMLString(pszWholeText));
1167 54 : VSIFree(pabyRet);
1168 :
1169 : // We assume parser will report errors via CPL.
1170 54 : if (psRoot.get() == nullptr)
1171 0 : return false;
1172 :
1173 106 : if (psRoot->eType != CXT_Element ||
1174 52 : !EQUAL(psRoot->pszValue, "GMLFeatureClassList"))
1175 : {
1176 2 : CPLError(CE_Failure, CPLE_AppDefined,
1177 : "File %s is not a GMLFeatureClassList document.", pszFile);
1178 2 : return false;
1179 : }
1180 :
1181 : const char *pszSequentialLayers =
1182 52 : CPLGetXMLValue(psRoot.get(), "SequentialLayers", nullptr);
1183 52 : if (pszSequentialLayers)
1184 8 : m_nHasSequentialLayers = CPLTestBool(pszSequentialLayers);
1185 :
1186 : /* -------------------------------------------------------------------- */
1187 : /* Extract feature classes for all definitions found. */
1188 : /* -------------------------------------------------------------------- */
1189 188 : for (CPLXMLNode *psThis = psRoot->psChild; psThis != nullptr;
1190 136 : psThis = psThis->psNext)
1191 : {
1192 136 : if (psThis->eType == CXT_Element &&
1193 109 : EQUAL(psThis->pszValue, "GMLFeatureClass"))
1194 : {
1195 101 : GMLFeatureClass *poClass = new GMLFeatureClass();
1196 :
1197 101 : if (!poClass->InitializeFromXML(psThis))
1198 : {
1199 0 : delete poClass;
1200 0 : return false;
1201 : }
1202 :
1203 101 : poClass->SetSchemaLocked(true);
1204 :
1205 101 : AddClass(poClass);
1206 : }
1207 : }
1208 :
1209 52 : SetClassListLocked(true);
1210 :
1211 52 : return true;
1212 : }
1213 :
1214 : /************************************************************************/
1215 : /* SaveClasses() */
1216 : /************************************************************************/
1217 :
1218 85 : bool GMLReader::SaveClasses(const char *pszFile)
1219 :
1220 : {
1221 : // Add logic later to determine reasonable default schema file.
1222 85 : if (pszFile == nullptr)
1223 0 : return false;
1224 :
1225 : /* -------------------------------------------------------------------- */
1226 : /* Create in memory schema tree. */
1227 : /* -------------------------------------------------------------------- */
1228 : CPLXMLNode *psRoot =
1229 85 : CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClassList");
1230 :
1231 85 : if (m_nHasSequentialLayers != -1 && m_nClassCount > 1)
1232 : {
1233 13 : CPLCreateXMLElementAndValue(psRoot, "SequentialLayers",
1234 13 : m_nHasSequentialLayers ? "true" : "false");
1235 : }
1236 :
1237 188 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
1238 : {
1239 103 : CPLAddXMLChild(psRoot, m_papoClass[iClass]->SerializeToXML());
1240 : }
1241 :
1242 : /* -------------------------------------------------------------------- */
1243 : /* Serialize to disk. */
1244 : /* -------------------------------------------------------------------- */
1245 85 : char *pszWholeText = CPLSerializeXMLTree(psRoot);
1246 :
1247 85 : CPLDestroyXMLNode(psRoot);
1248 :
1249 85 : auto fp = VSIVirtualHandleUniquePtr(VSIFOpenL(pszFile, "wb"));
1250 :
1251 85 : bool bSuccess = true;
1252 85 : if (fp == nullptr)
1253 0 : bSuccess = false;
1254 : else
1255 : {
1256 85 : if (fp->Write(pszWholeText, strlen(pszWholeText), 1) != 1)
1257 0 : bSuccess = false;
1258 : }
1259 :
1260 85 : CPLFree(pszWholeText);
1261 :
1262 85 : return bSuccess;
1263 : }
1264 :
1265 : /************************************************************************/
1266 : /* PrescanForSchema() */
1267 : /* */
1268 : /* For now we use a pretty dumb approach of just doing a normal */
1269 : /* scan of the whole file, building up the schema information. */
1270 : /* Eventually we hope to do a more efficient scan when just */
1271 : /* looking for schema information. */
1272 : /************************************************************************/
1273 :
1274 103 : bool GMLReader::PrescanForSchema(bool bGetExtents, bool bOnlyDetectSRS)
1275 :
1276 : {
1277 103 : if (m_pszFilename == nullptr)
1278 0 : return false;
1279 :
1280 103 : if (!bOnlyDetectSRS)
1281 : {
1282 94 : SetClassListLocked(false);
1283 94 : ClearClasses();
1284 : }
1285 :
1286 103 : if (!SetupParser())
1287 0 : return false;
1288 :
1289 103 : m_bCanUseGlobalSRSName = true;
1290 :
1291 103 : GMLFeatureClass *poLastClass = nullptr;
1292 :
1293 103 : m_nHasSequentialLayers = TRUE;
1294 :
1295 103 : void *hCacheSRS = GML_BuildOGRGeometryFromList_CreateCache();
1296 :
1297 206 : std::string osWork;
1298 :
1299 112 : for (int i = 0; i < m_nClassCount; i++)
1300 : {
1301 9 : m_papoClass[i]->SetFeatureCount(-1);
1302 9 : m_papoClass[i]->SetSRSName(nullptr);
1303 : }
1304 :
1305 103 : GMLFeature *poFeature = nullptr;
1306 103 : std::set<GMLFeatureClass *> knownClasses;
1307 103 : bool bFoundPerFeatureSRSName = false;
1308 :
1309 598 : while ((poFeature = NextFeature()) != nullptr)
1310 : {
1311 495 : GMLFeatureClass *poClass = poFeature->GetClass();
1312 :
1313 495 : if (knownClasses.find(poClass) == knownClasses.end())
1314 : {
1315 116 : knownClasses.insert(poClass);
1316 116 : if (m_pszGlobalSRSName && GML_IsLegitSRSName(m_pszGlobalSRSName))
1317 : {
1318 37 : poClass->SetSRSName(m_pszGlobalSRSName);
1319 : }
1320 : }
1321 :
1322 524 : if (poLastClass != nullptr && poClass != poLastClass &&
1323 29 : poClass->GetFeatureCount() != -1)
1324 0 : m_nHasSequentialLayers = false;
1325 495 : poLastClass = poClass;
1326 :
1327 495 : if (poClass->GetFeatureCount() == -1)
1328 116 : poClass->SetFeatureCount(1);
1329 : else
1330 379 : poClass->SetFeatureCount(poClass->GetFeatureCount() + 1);
1331 :
1332 495 : const CPLXMLNode *const *papsGeometry = poFeature->GetGeometryList();
1333 495 : bool bGeometryColumnJustCreated = false;
1334 :
1335 495 : const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
1336 : const CPLXMLNode *psBoundedByGeometry =
1337 495 : poFeature->GetBoundedByGeometry();
1338 495 : int nFeatureGeomCount = poFeature->GetGeometryCount();
1339 495 : if (psBoundedByGeometry && nFeatureGeomCount == 0 &&
1340 3 : strcmp(psBoundedByGeometry->pszValue, "null") != 0)
1341 : {
1342 1 : apsGeometries[0] = psBoundedByGeometry;
1343 1 : papsGeometry = apsGeometries;
1344 1 : nFeatureGeomCount = 1;
1345 : }
1346 :
1347 495 : if (!bOnlyDetectSRS && papsGeometry != nullptr &&
1348 476 : papsGeometry[0] != nullptr)
1349 : {
1350 437 : if (poClass->GetGeometryPropertyCount() == 0)
1351 : {
1352 176 : std::string osPath(poClass->GetSingleGeomElemPath());
1353 109 : if (osPath.empty() &&
1354 109 : poClass->IsConsistentSingleGeomElemPath() &&
1355 17 : papsGeometry[0] == psBoundedByGeometry)
1356 : {
1357 1 : osPath = "boundedBy";
1358 : }
1359 88 : std::string osGeomName(osPath);
1360 88 : const auto nPos = osGeomName.rfind('|');
1361 88 : if (nPos != std::string::npos)
1362 2 : osGeomName = osGeomName.substr(nPos + 1);
1363 88 : bGeometryColumnJustCreated = true;
1364 176 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1365 176 : osGeomName.c_str(), osPath.c_str(), wkbUnknown, -1, true));
1366 : }
1367 : }
1368 :
1369 495 : if (bGetExtents && papsGeometry != nullptr)
1370 : {
1371 : const int nIters = std::min(nFeatureGeomCount,
1372 495 : poClass->GetGeometryPropertyCount());
1373 959 : for (int i = 0; i < nIters; ++i)
1374 : {
1375 464 : if (papsGeometry[i] == nullptr)
1376 7 : continue;
1377 :
1378 459 : const CPLXMLNode *myGeometryList[2] = {papsGeometry[i],
1379 459 : nullptr};
1380 918 : OGRGeometry *poGeometry = GML_BuildOGRGeometryFromList(
1381 459 : myGeometryList, true, m_bInvertAxisOrderIfLatLong, nullptr,
1382 459 : m_bConsiderEPSGAsURN, m_eSwapCoordinates,
1383 459 : m_bGetSecondaryGeometryOption, hCacheSRS,
1384 459 : m_bFaceHoleNegative);
1385 459 : if (poGeometry == nullptr)
1386 2 : continue;
1387 :
1388 457 : auto poGeomProperty = poClass->GetGeometryProperty(i);
1389 : OGRwkbGeometryType eGType =
1390 457 : static_cast<OGRwkbGeometryType>(poGeomProperty->GetType());
1391 :
1392 914 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
1393 457 : myGeometryList, osWork, m_bConsiderEPSGAsURN);
1394 457 : if (pszSRSName != nullptr)
1395 138 : bFoundPerFeatureSRSName = true;
1396 :
1397 457 : if (pszSRSName != nullptr && m_pszGlobalSRSName != nullptr &&
1398 73 : !EQUAL(pszSRSName, m_pszGlobalSRSName))
1399 : {
1400 0 : m_bCanUseGlobalSRSName = false;
1401 : }
1402 457 : if (m_pszGlobalSRSName == nullptr || pszSRSName != nullptr)
1403 : {
1404 201 : if (poClass->GetGeometryPropertyCount() == 1)
1405 192 : poClass->MergeSRSName(pszSRSName);
1406 : else
1407 9 : poGeomProperty->MergeSRSName(pszSRSName ? pszSRSName
1408 : : "");
1409 : }
1410 :
1411 : // Merge geometry type into layer.
1412 457 : if (bGeometryColumnJustCreated)
1413 : {
1414 86 : poGeomProperty->SetType(poGeometry->getGeometryType());
1415 : }
1416 : else
1417 : {
1418 371 : poGeomProperty->SetType(OGRMergeGeometryTypesEx(
1419 371 : eGType, poGeometry->getGeometryType(), true));
1420 : }
1421 :
1422 : // Merge extents.
1423 457 : if (!poGeometry->IsEmpty())
1424 : {
1425 456 : double dfXMin = 0.0;
1426 456 : double dfXMax = 0.0;
1427 456 : double dfYMin = 0.0;
1428 456 : double dfYMax = 0.0;
1429 :
1430 456 : OGREnvelope sEnvelope;
1431 :
1432 456 : poGeometry->getEnvelope(&sEnvelope);
1433 456 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
1434 : {
1435 361 : dfXMin = std::min(dfXMin, sEnvelope.MinX);
1436 361 : dfXMax = std::max(dfXMax, sEnvelope.MaxX);
1437 361 : dfYMin = std::min(dfYMin, sEnvelope.MinY);
1438 361 : dfYMax = std::max(dfYMax, sEnvelope.MaxY);
1439 : }
1440 : else
1441 : {
1442 95 : dfXMin = sEnvelope.MinX;
1443 95 : dfXMax = sEnvelope.MaxX;
1444 95 : dfYMin = sEnvelope.MinY;
1445 95 : dfYMax = sEnvelope.MaxY;
1446 : }
1447 :
1448 456 : poClass->SetExtents(dfXMin, dfXMax, dfYMin, dfYMax);
1449 : }
1450 457 : delete poGeometry;
1451 : }
1452 : }
1453 :
1454 495 : delete poFeature;
1455 : }
1456 :
1457 103 : GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS);
1458 :
1459 103 : if (bGetExtents && m_bCanUseGlobalSRSName && m_pszGlobalSRSName &&
1460 26 : !bFoundPerFeatureSRSName && m_bInvertAxisOrderIfLatLong &&
1461 219 : GML_IsLegitSRSName(m_pszGlobalSRSName) &&
1462 13 : GML_IsSRSLatLongOrder(m_pszGlobalSRSName))
1463 : {
1464 : /* So when we have computed the extent, we didn't know yet */
1465 : /* the SRS to use. Now we know it, we have to fix the extent */
1466 : /* order */
1467 :
1468 6 : for (int i = 0; i < m_nClassCount; i++)
1469 : {
1470 3 : GMLFeatureClass *poClass = m_papoClass[i];
1471 3 : if (poClass->HasExtents())
1472 : {
1473 2 : double dfXMin = 0.0;
1474 2 : double dfXMax = 0.0;
1475 2 : double dfYMin = 0.0;
1476 2 : double dfYMax = 0.0;
1477 2 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
1478 2 : poClass->SetExtents(dfYMin, dfYMax, dfXMin, dfXMax);
1479 : }
1480 : }
1481 : }
1482 :
1483 103 : CleanupParser();
1484 :
1485 103 : return true;
1486 : }
1487 :
1488 : /************************************************************************/
1489 : /* ResetReading() */
1490 : /************************************************************************/
1491 :
1492 708 : void GMLReader::ResetReading()
1493 :
1494 : {
1495 708 : CleanupParser();
1496 708 : SetFilteredClassName(nullptr);
1497 708 : }
1498 :
1499 : /************************************************************************/
1500 : /* SetGlobalSRSName() */
1501 : /************************************************************************/
1502 :
1503 193 : void GMLReader::SetGlobalSRSName(const char *pszGlobalSRSName)
1504 : {
1505 193 : if (m_pszGlobalSRSName == nullptr && pszGlobalSRSName != nullptr)
1506 : {
1507 59 : const char *pszVertCS_EPSG = nullptr;
1508 73 : if (STARTS_WITH(pszGlobalSRSName, "EPSG:") &&
1509 14 : (pszVertCS_EPSG = strstr(pszGlobalSRSName, ", EPSG:")) != nullptr)
1510 : {
1511 2 : m_pszGlobalSRSName =
1512 2 : CPLStrdup(CPLSPrintf("EPSG:%d+%d", atoi(pszGlobalSRSName + 5),
1513 : atoi(pszVertCS_EPSG + 7)));
1514 : }
1515 57 : else if (STARTS_WITH(pszGlobalSRSName, "EPSG:") && m_bConsiderEPSGAsURN)
1516 : {
1517 5 : m_pszGlobalSRSName = CPLStrdup(
1518 : CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszGlobalSRSName + 5));
1519 : }
1520 : else
1521 : {
1522 52 : m_pszGlobalSRSName = CPLStrdup(pszGlobalSRSName);
1523 : }
1524 59 : m_bCanUseGlobalSRSName = m_pszGlobalSRSName != nullptr;
1525 : }
1526 193 : }
1527 :
1528 : /************************************************************************/
1529 : /* SetFilteredClassName() */
1530 : /************************************************************************/
1531 :
1532 786 : bool GMLReader::SetFilteredClassName(const char *pszClassName)
1533 : {
1534 786 : CPLFree(m_pszFilteredClassName);
1535 786 : m_pszFilteredClassName = pszClassName ? CPLStrdup(pszClassName) : nullptr;
1536 :
1537 786 : m_nFilteredClassIndex = -1;
1538 786 : if (m_pszFilteredClassName != nullptr)
1539 : {
1540 534 : for (int i = 0; i < m_nClassCount; i++)
1541 : {
1542 1002 : if (strcmp(m_papoClass[i]->GetElementName(),
1543 501 : m_pszFilteredClassName) == 0)
1544 : {
1545 45 : m_nFilteredClassIndex = i;
1546 45 : break;
1547 : }
1548 : }
1549 : }
1550 :
1551 786 : return true;
1552 : }
|