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