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 451 : IGMLReader::~IGMLReader()
42 : {
43 451 : }
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 446 : 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 446 : bGetSecondaryGeometryOption);
89 : }
90 :
91 : #endif
92 :
93 : CPLMutex *GMLReader::hMutex = nullptr;
94 :
95 : /************************************************************************/
96 : /* GMLReader() */
97 : /************************************************************************/
98 :
99 446 : GMLReader::GMLReader(
100 : #if !defined(HAVE_EXPAT) || !defined(HAVE_XERCES)
101 : CPL_UNUSED
102 : #endif
103 : bool bUseExpatParserPreferably,
104 : bool bInvertAxisOrderIfLatLong, bool bConsiderEPSGAsURN,
105 446 : GMLSwapCoordinatesEnum eSwapCoordinates, bool bGetSecondaryGeometryOption)
106 : : // Experimental. Not publicly advertized. See commented doc in
107 : // drv_gml.html
108 : m_bFetchAllGeometries(
109 446 : 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 892 : CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
118 : {
119 : #ifndef HAVE_XERCES
120 : #else
121 : #ifdef HAVE_EXPAT
122 446 : if (bUseExpatParserPreferably)
123 444 : bUseExpatReader = true;
124 : #endif
125 : #endif
126 :
127 : #if defined(HAVE_EXPAT) && defined(HAVE_XERCES)
128 446 : if (bUseExpatReader)
129 444 : CPLDebug("GML", "Using Expat reader");
130 : else
131 2 : CPLDebug("GML", "Using Xerces reader");
132 : #endif
133 446 : }
134 :
135 : /************************************************************************/
136 : /* ~GMLReader() */
137 : /************************************************************************/
138 :
139 892 : GMLReader::~GMLReader()
140 :
141 : {
142 446 : GMLReader::ClearClasses();
143 :
144 446 : CPLFree(m_pszFilename);
145 :
146 446 : CleanupParser();
147 :
148 446 : delete m_poRecycledState;
149 :
150 : #ifdef HAVE_XERCES
151 446 : if (m_bXercesInitialized)
152 2 : OGRDeinitializeXerces();
153 : #endif
154 : #ifdef HAVE_EXPAT
155 446 : CPLFree(pabyBuf);
156 : #endif
157 :
158 446 : if (fpGML)
159 445 : VSIFCloseL(fpGML);
160 446 : fpGML = nullptr;
161 :
162 446 : CPLFree(m_pszGlobalSRSName);
163 :
164 446 : CPLFree(m_pszFilteredClassName);
165 892 : }
166 :
167 : /************************************************************************/
168 : /* SetSourceFile() */
169 : /************************************************************************/
170 :
171 451 : void GMLReader::SetSourceFile(const char *pszFilename)
172 :
173 : {
174 451 : CPLFree(m_pszFilename);
175 451 : m_pszFilename = CPLStrdup(pszFilename);
176 451 : }
177 :
178 : /************************************************************************/
179 : /* GetSourceFileName() */
180 : /************************************************************************/
181 :
182 445 : const char *GMLReader::GetSourceFileName()
183 : {
184 445 : return m_pszFilename;
185 : }
186 :
187 : /************************************************************************/
188 : /* SetFP() */
189 : /************************************************************************/
190 :
191 427 : void GMLReader::SetFP(VSILFILE *fp)
192 : {
193 427 : fpGML = fp;
194 427 : }
195 :
196 : /************************************************************************/
197 : /* SetupParser() */
198 : /************************************************************************/
199 :
200 584 : bool GMLReader::SetupParser()
201 :
202 : {
203 584 : if (fpGML == nullptr)
204 23 : fpGML = VSIFOpenL(m_pszFilename, "rt");
205 584 : if (fpGML != nullptr)
206 584 : VSIFSeekL(fpGML, 0, SEEK_SET);
207 :
208 584 : int bRet = -1;
209 : #ifdef HAVE_EXPAT
210 584 : if (bUseExpatReader)
211 582 : bRet = SetupParserExpat();
212 : #endif
213 :
214 : #ifdef HAVE_XERCES
215 584 : if (!bUseExpatReader)
216 2 : bRet = SetupParserXerces();
217 : #endif
218 584 : if (bRet < 0)
219 : {
220 0 : CPLError(CE_Failure, CPLE_AppDefined,
221 : "SetupParser(): should not happen");
222 0 : return false;
223 : }
224 :
225 584 : if (!bRet)
226 0 : return false;
227 :
228 584 : m_bReadStarted = false;
229 :
230 : // Push an empty state.
231 584 : PushState(m_poRecycledState ? m_poRecycledState : new GMLReadState());
232 584 : m_poRecycledState = nullptr;
233 :
234 584 : 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 582 : bool GMLReader::SetupParserExpat()
320 : {
321 : // Cleanup any old parser.
322 582 : if (oParser != nullptr)
323 5 : CleanupParser();
324 :
325 582 : oParser = OGRCreateExpatXMLParser();
326 582 : m_poGMLHandler = new GMLExpatHandler(this, oParser);
327 :
328 582 : XML_SetElementHandler(oParser, GMLExpatHandler::startElementCbk,
329 : GMLExpatHandler::endElementCbk);
330 582 : XML_SetCharacterDataHandler(oParser, GMLExpatHandler::dataHandlerCbk);
331 582 : XML_SetUserData(oParser, m_poGMLHandler);
332 :
333 582 : if (pabyBuf == nullptr)
334 348 : pabyBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(PARSER_BUF_SIZE));
335 582 : if (pabyBuf == nullptr)
336 0 : return false;
337 :
338 582 : return true;
339 : }
340 : #endif
341 :
342 : /************************************************************************/
343 : /* CleanupParser() */
344 : /************************************************************************/
345 :
346 1333 : void GMLReader::CleanupParser()
347 :
348 : {
349 : #ifdef HAVE_XERCES
350 1333 : if (!bUseExpatReader && m_poSAXReader == nullptr)
351 3 : return;
352 : #endif
353 :
354 : #ifdef HAVE_EXPAT
355 1330 : if (bUseExpatReader && oParser == nullptr)
356 746 : return;
357 : #endif
358 :
359 1174 : while (m_poState)
360 590 : PopState();
361 :
362 : #ifdef HAVE_XERCES
363 584 : delete m_poSAXReader;
364 584 : m_poSAXReader = nullptr;
365 584 : OGRDestroyXercesInputSource(m_GMLInputSource);
366 584 : m_GMLInputSource = nullptr;
367 584 : delete m_poCompleteFeature;
368 584 : m_poCompleteFeature = nullptr;
369 584 : m_bEOF = false;
370 : #endif
371 :
372 : #ifdef HAVE_EXPAT
373 584 : if (oParser)
374 582 : XML_ParserFree(oParser);
375 584 : oParser = nullptr;
376 :
377 1231 : for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
378 647 : delete ppoFeatureTab[i];
379 584 : CPLFree(ppoFeatureTab);
380 584 : nFeatureTabIndex = 0;
381 584 : nFeatureTabLength = 0;
382 584 : nFeatureTabAlloc = 0;
383 584 : ppoFeatureTab = nullptr;
384 584 : m_osErrorMessage.clear();
385 :
386 : #endif
387 :
388 584 : delete m_poGMLHandler;
389 584 : m_poGMLHandler = nullptr;
390 :
391 584 : 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 2767 : GMLFeature *GMLReader::NextFeatureExpat()
456 :
457 : {
458 2767 : if (!m_bReadStarted)
459 : {
460 582 : if (oParser == nullptr)
461 450 : SetupParser();
462 :
463 582 : m_bReadStarted = true;
464 : }
465 :
466 2767 : if (nFeatureTabIndex < nFeatureTabLength)
467 : {
468 1963 : return ppoFeatureTab[nFeatureTabIndex++];
469 : }
470 :
471 804 : 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 1386 : if (fpGML == nullptr || m_bStopParsing || VSIFEofL(fpGML) ||
479 584 : VSIFErrorL(fpGML))
480 218 : return nullptr;
481 :
482 584 : nFeatureTabLength = 0;
483 584 : nFeatureTabIndex = 0;
484 :
485 584 : int nDone = 0;
486 0 : do
487 : {
488 : // Reset counter that is used to detect billion laugh attacks.
489 584 : static_cast<GMLExpatHandler *>(m_poGMLHandler)
490 584 : ->ResetDataHandlerCounter();
491 :
492 : unsigned int nLen = static_cast<unsigned int>(
493 584 : VSIFReadL(pabyBuf, 1, PARSER_BUF_SIZE, fpGML));
494 584 : 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 584 : while (nDone && nLen > 0 && pabyBuf[nLen - 1] == '\0')
501 0 : nLen--;
502 :
503 584 : 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 584 : if (!m_bStopParsing)
516 579 : m_bStopParsing = static_cast<GMLExpatHandler *>(m_poGMLHandler)
517 579 : ->HasStoppedParsing();
518 584 : } while (!nDone && !m_bStopParsing && nFeatureTabLength == 0);
519 :
520 584 : if (nFeatureTabLength)
521 540 : 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 2769 : GMLFeature *GMLReader::NextFeature()
534 : {
535 : #ifdef HAVE_EXPAT
536 2769 : if (bUseExpatReader)
537 2767 : 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 3151 : void GMLReader::PushFeature(const char *pszElement, const char *pszFID,
565 : int nClassIndex)
566 :
567 : {
568 3151 : int iClass = 0;
569 :
570 3151 : if (nClassIndex != INT_MAX)
571 : {
572 1278 : iClass = nClassIndex;
573 : }
574 : else
575 : {
576 : /* --------------------------------------------------------------------
577 : */
578 : /* Find the class of this element. */
579 : /* --------------------------------------------------------------------
580 : */
581 4247 : for (; iClass < m_nClassCount; iClass++)
582 : {
583 4102 : if (EQUAL(pszElement, m_papoClass[iClass]->GetElementName()))
584 1728 : 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 1873 : if (iClass == m_nClassCount)
594 : {
595 145 : CPLAssert(!m_bClassListLocked);
596 :
597 145 : GMLFeatureClass *poNewClass = new GMLFeatureClass(pszElement);
598 :
599 145 : AddClass(poNewClass);
600 : }
601 : }
602 :
603 : /* -------------------------------------------------------------------- */
604 : /* Create a feature of this feature class. Try to set the fid */
605 : /* if available. */
606 : /* -------------------------------------------------------------------- */
607 3151 : GMLFeature *poFeature = new GMLFeature(m_papoClass[iClass]);
608 3151 : if (pszFID != nullptr)
609 : {
610 1615 : poFeature->SetFID(pszFID);
611 : }
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Create and push a new read state. */
615 : /* -------------------------------------------------------------------- */
616 : GMLReadState *poState =
617 3151 : m_poRecycledState ? m_poRecycledState : new GMLReadState();
618 3151 : m_poRecycledState = nullptr;
619 3151 : poState->m_poFeature = poFeature;
620 3151 : PushState(poState);
621 3151 : }
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 6026 : int GMLReader::GetFeatureElementIndex(const char *pszElement,
631 : int nElementLength,
632 : GMLAppSchemaType eAppSchemaType)
633 :
634 : {
635 6026 : const char *pszLast = m_poState->GetLastComponent();
636 6026 : const size_t nLenLast = m_poState->GetLastComponentLen();
637 :
638 6026 : if (eAppSchemaType == APPSCHEMA_MTKGML)
639 : {
640 44 : if (m_poState->m_nPathLength != 1)
641 22 : return -1;
642 : }
643 5982 : else if ((nLenLast >= 6 && EQUAL(pszLast + nLenLast - 6, "member")) ||
644 1965 : (nLenLast >= 7 && EQUAL(pszLast + nLenLast - 7, "members")))
645 : {
646 : // Default feature name.
647 : }
648 : else
649 : {
650 2695 : if (nLenLast == 4 && strcmp(pszLast, "dane") == 0)
651 : {
652 : // Polish TBD GML.
653 : }
654 :
655 : // Begin of OpenLS.
656 2695 : else if (nLenLast == 19 && nElementLength == 15 &&
657 1 : strcmp(pszLast, "GeocodeResponseList") == 0 &&
658 0 : strcmp(pszElement, "GeocodedAddress") == 0)
659 : {
660 : }
661 2695 : 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 2692 : 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 2683 : else if (nLenLast > 6 &&
678 517 : 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 2683 : 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 2679 : 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 2675 : 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 2631 : if (m_bClassListLocked)
709 : {
710 12397 : for (int i = 0; i < m_nClassCount; i++)
711 : {
712 10849 : if (m_poState->osPath.size() + 1 + nElementLength ==
713 10849 : 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 11110 : 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 1002 : return -1;
736 : }
737 : }
738 : }
739 :
740 : // If the class list isn't locked, any element that is a featureMember
741 : // will do.
742 4920 : if (!m_bClassListLocked)
743 1873 : return INT_MAX;
744 :
745 : // otherwise, find a class with the desired element name.
746 17229 : for (int i = 0; i < m_nClassCount; i++)
747 : {
748 31350 : if (nElementLength ==
749 18070 : static_cast<int>(m_papoClass[i]->GetElementNameLen()) &&
750 2395 : memcmp(pszElement, m_papoClass[i]->GetElementName(),
751 : nElementLength) == 0)
752 1493 : return i;
753 : }
754 :
755 1554 : return -1;
756 : }
757 :
758 : /************************************************************************/
759 : /* IsCityGMLGenericAttributeElement() */
760 : /************************************************************************/
761 :
762 136 : bool GMLReader::IsCityGMLGenericAttributeElement(const char *pszElement,
763 : void *attr)
764 :
765 : {
766 136 : if (strcmp(pszElement, "stringAttribute") != 0 &&
767 98 : strcmp(pszElement, "intAttribute") != 0 &&
768 96 : strcmp(pszElement, "doubleAttribute") != 0)
769 94 : 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 13429 : int GMLReader::GetAttributeElementIndex(const char *pszElement, int nLen,
803 : const char *pszAttrKey)
804 :
805 : {
806 13429 : 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 13429 : if (!poClass->IsSchemaLocked())
811 7728 : return INT_MAX;
812 :
813 : // Otherwise build the path to this element into a single string
814 : // and compare against known attributes.
815 5701 : if (m_poState->m_nPathLength == 0)
816 : {
817 3837 : if (pszAttrKey == nullptr)
818 3764 : 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 1864 : int nFullLen = nLen + static_cast<int>(m_poState->osPath.size()) + 1;
833 1864 : if (pszAttrKey != nullptr)
834 89 : nFullLen += 1 + static_cast<int>(strlen(pszAttrKey));
835 1864 : osElemPath.reserve(nFullLen);
836 1864 : osElemPath.assign(m_poState->osPath);
837 1864 : osElemPath.append(1, '|');
838 1864 : osElemPath.append(pszElement, nLen);
839 1864 : if (pszAttrKey != nullptr)
840 : {
841 89 : osElemPath.append(1, '@');
842 89 : osElemPath.append(pszAttrKey);
843 : }
844 1864 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
845 1864 : nFullLen);
846 : }
847 : }
848 :
849 : /************************************************************************/
850 : /* PopState() */
851 : /************************************************************************/
852 :
853 3735 : void GMLReader::PopState()
854 :
855 : {
856 3735 : if (m_poState != nullptr)
857 : {
858 : #ifdef HAVE_XERCES
859 3735 : 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 3734 : 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 3735 : if (bUseExpatReader && m_poState->m_poFeature != nullptr)
874 : {
875 3150 : if (nFeatureTabLength >= nFeatureTabAlloc)
876 : {
877 609 : nFeatureTabAlloc = nFeatureTabLength * 4 / 3 + 16;
878 609 : ppoFeatureTab = static_cast<GMLFeature **>(CPLRealloc(
879 609 : ppoFeatureTab, sizeof(GMLFeature *) * (nFeatureTabAlloc)));
880 : }
881 3150 : ppoFeatureTab[nFeatureTabLength] = m_poState->m_poFeature;
882 3150 : nFeatureTabLength++;
883 :
884 3150 : m_poState->m_poFeature = nullptr;
885 : }
886 : #endif
887 :
888 3735 : GMLReadState *poParent = m_poState->m_poParentState;
889 :
890 3735 : delete m_poRecycledState;
891 3735 : m_poRecycledState = m_poState;
892 3735 : m_poRecycledState->Reset();
893 3735 : m_poState = poParent;
894 : }
895 3735 : }
896 :
897 : /************************************************************************/
898 : /* PushState() */
899 : /************************************************************************/
900 :
901 3735 : void GMLReader::PushState(GMLReadState *poState)
902 :
903 : {
904 3735 : poState->m_poParentState = m_poState;
905 3735 : m_poState = poState;
906 3735 : }
907 :
908 : /************************************************************************/
909 : /* GetClass() */
910 : /************************************************************************/
911 :
912 894 : GMLFeatureClass *GMLReader::GetClass(int iClass) const
913 :
914 : {
915 894 : if (iClass < 0 || iClass >= m_nClassCount)
916 0 : return nullptr;
917 :
918 894 : return m_papoClass[iClass];
919 : }
920 :
921 : /************************************************************************/
922 : /* GetClass() */
923 : /************************************************************************/
924 :
925 1701 : GMLFeatureClass *GMLReader::GetClass(const char *pszName) const
926 :
927 : {
928 2861 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
929 : {
930 1766 : if (EQUAL(m_papoClass[iClass]->GetName(), pszName))
931 606 : return m_papoClass[iClass];
932 : }
933 :
934 1095 : return nullptr;
935 : }
936 :
937 : /************************************************************************/
938 : /* AddClass() */
939 : /************************************************************************/
940 :
941 698 : int GMLReader::AddClass(GMLFeatureClass *poNewClass)
942 :
943 : {
944 698 : CPLAssert(GetClass(poNewClass->GetName()) == nullptr);
945 :
946 698 : m_nClassCount++;
947 698 : m_papoClass = static_cast<GMLFeatureClass **>(
948 698 : CPLRealloc(m_papoClass, sizeof(void *) * m_nClassCount));
949 698 : m_papoClass[m_nClassCount - 1] = poNewClass;
950 :
951 698 : if (poNewClass->HasFeatureProperties())
952 8 : m_bLookForClassAtAnyLevel = true;
953 :
954 698 : return m_nClassCount - 1;
955 : }
956 :
957 : /************************************************************************/
958 : /* ClearClasses() */
959 : /************************************************************************/
960 :
961 606 : void GMLReader::ClearClasses()
962 :
963 : {
964 1296 : for (int i = 0; i < m_nClassCount; i++)
965 690 : delete m_papoClass[i];
966 606 : CPLFree(m_papoClass);
967 :
968 606 : m_nClassCount = 0;
969 606 : m_papoClass = nullptr;
970 606 : m_bLookForClassAtAnyLevel = false;
971 606 : }
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 8202 : void GMLReader::SetFeaturePropertyDirectly(const char *pszElement,
982 : char *pszValue, int iPropertyIn,
983 : GMLPropertyType eType)
984 :
985 : {
986 8202 : GMLFeature *poFeature = GetState()->m_poFeature;
987 :
988 8202 : CPLAssert(poFeature != nullptr);
989 :
990 : /* -------------------------------------------------------------------- */
991 : /* Does this property exist in the feature class? If not, add */
992 : /* it. */
993 : /* -------------------------------------------------------------------- */
994 8202 : GMLFeatureClass *poClass = poFeature->GetClass();
995 8202 : int iProperty = 0;
996 :
997 8202 : const int nPropertyCount = poClass->GetPropertyCount();
998 8202 : if (iPropertyIn >= 0 && iPropertyIn < nPropertyCount)
999 : {
1000 3064 : iProperty = iPropertyIn;
1001 : }
1002 : else
1003 : {
1004 13673 : for (; iProperty < nPropertyCount; iProperty++)
1005 : {
1006 13224 : if (strcmp(poClass->GetProperty(iProperty)->GetSrcElement(),
1007 13224 : pszElement) == 0)
1008 4689 : break;
1009 : }
1010 :
1011 5138 : if (iProperty == nPropertyCount)
1012 : {
1013 449 : 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 448 : CPLString osFieldName;
1023 :
1024 448 : 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 436 : else if (strchr(pszElement, '|') == nullptr)
1047 : {
1048 337 : osFieldName = pszElement;
1049 : }
1050 : else
1051 : {
1052 99 : osFieldName = strrchr(pszElement, '|') + 1;
1053 99 : if (poClass->GetPropertyIndex(osFieldName) != -1)
1054 12 : osFieldName = pszElement;
1055 : }
1056 :
1057 448 : size_t nPos = osFieldName.find("@");
1058 448 : if (nPos != std::string::npos)
1059 3 : osFieldName[nPos] = '_';
1060 :
1061 : // Does this conflict with an existing property name?
1062 449 : 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 448 : new GMLPropertyDefn(osFieldName, pszElement);
1076 :
1077 448 : if (EQUAL(CPLGetConfigOption("GML_FIELDTYPES", ""),
1078 : "ALWAYS_STRING"))
1079 0 : poPDefn->SetType(GMLPT_String);
1080 448 : else if (eType != GMLPT_Untyped)
1081 3 : poPDefn->SetType(eType);
1082 :
1083 448 : 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 8201 : poFeature->SetPropertyDirectly(iProperty, pszValue);
1096 :
1097 : /* -------------------------------------------------------------------- */
1098 : /* Do we need to update the property type? */
1099 : /* -------------------------------------------------------------------- */
1100 8201 : if (!poClass->IsSchemaLocked() && !EQUAL(pszValue, OGR_GML_NULL))
1101 : {
1102 4995 : auto poClassProperty = poClass->GetProperty(iProperty);
1103 4995 : if (poClassProperty)
1104 : {
1105 4995 : const GMLProperty *poProp = poFeature->GetProperty(iProperty);
1106 4995 : if (poProp)
1107 : {
1108 4995 : poClassProperty->AnalysePropertyValue(poProp, m_bSetWidthFlag);
1109 : }
1110 : }
1111 : else
1112 : {
1113 0 : CPLAssert(false);
1114 : }
1115 : }
1116 : }
1117 :
1118 : /************************************************************************/
1119 : /* LoadClasses() */
1120 : /************************************************************************/
1121 :
1122 61 : bool GMLReader::LoadClasses(const char *pszFile)
1123 :
1124 : {
1125 : // Add logic later to determine reasonable default schema file.
1126 61 : if (pszFile == nullptr)
1127 0 : return false;
1128 :
1129 : /* -------------------------------------------------------------------- */
1130 : /* Load the raw XML file. */
1131 : /* -------------------------------------------------------------------- */
1132 61 : GByte *pabyRet = nullptr;
1133 61 : const char *pszWholeText = nullptr;
1134 : {
1135 : #ifdef EMBED_RESOURCE_FILES
1136 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1137 : #endif
1138 61 : if (VSIIngestFile(nullptr, pszFile, &pabyRet, nullptr,
1139 61 : 100 * 1024 * 1024))
1140 : {
1141 61 : pszWholeText = reinterpret_cast<const char *>(pabyRet);
1142 : }
1143 : else
1144 : {
1145 : #ifdef EMBED_RESOURCE_FILES
1146 : pszWholeText = GMLGetFileContent(pszFile);
1147 : if (pszWholeText)
1148 : {
1149 : CPLDebug("GML", "Using embedded %s", pszFile);
1150 : }
1151 : #endif
1152 : }
1153 : }
1154 61 : if (!pszWholeText)
1155 0 : return false;
1156 :
1157 61 : if (strstr(pszWholeText, "<GMLFeatureClassList") == nullptr)
1158 : {
1159 0 : VSIFree(pabyRet);
1160 0 : CPLError(CE_Failure, CPLE_AppDefined,
1161 : "File %s does not contain a GMLFeatureClassList tree.",
1162 : pszFile);
1163 0 : return false;
1164 : }
1165 :
1166 : /* -------------------------------------------------------------------- */
1167 : /* Convert to XML parse tree. */
1168 : /* -------------------------------------------------------------------- */
1169 122 : CPLXMLTreeCloser psRoot(CPLParseXMLString(pszWholeText));
1170 61 : VSIFree(pabyRet);
1171 :
1172 : // We assume parser will report errors via CPL.
1173 61 : if (psRoot.get() == nullptr)
1174 0 : return false;
1175 :
1176 120 : if (psRoot->eType != CXT_Element ||
1177 59 : !EQUAL(psRoot->pszValue, "GMLFeatureClassList"))
1178 : {
1179 2 : CPLError(CE_Failure, CPLE_AppDefined,
1180 : "File %s is not a GMLFeatureClassList document.", pszFile);
1181 2 : return false;
1182 : }
1183 :
1184 : const char *pszSequentialLayers =
1185 59 : CPLGetXMLValue(psRoot.get(), "SequentialLayers", nullptr);
1186 59 : if (pszSequentialLayers)
1187 10 : m_nHasSequentialLayers = CPLTestBool(pszSequentialLayers);
1188 :
1189 : /* -------------------------------------------------------------------- */
1190 : /* Extract feature classes for all definitions found. */
1191 : /* -------------------------------------------------------------------- */
1192 206 : for (CPLXMLNode *psThis = psRoot->psChild; psThis != nullptr;
1193 147 : psThis = psThis->psNext)
1194 : {
1195 147 : if (psThis->eType == CXT_Element &&
1196 120 : EQUAL(psThis->pszValue, "GMLFeatureClass"))
1197 : {
1198 110 : GMLFeatureClass *poClass = new GMLFeatureClass();
1199 :
1200 110 : if (!poClass->InitializeFromXML(psThis))
1201 : {
1202 0 : delete poClass;
1203 0 : return false;
1204 : }
1205 :
1206 110 : poClass->SetSchemaLocked(true);
1207 :
1208 110 : AddClass(poClass);
1209 : }
1210 : }
1211 :
1212 59 : SetClassListLocked(true);
1213 :
1214 59 : return true;
1215 : }
1216 :
1217 : /************************************************************************/
1218 : /* SaveClasses() */
1219 : /************************************************************************/
1220 :
1221 103 : bool GMLReader::SaveClasses(const char *pszFile)
1222 :
1223 : {
1224 : // Add logic later to determine reasonable default schema file.
1225 103 : if (pszFile == nullptr)
1226 0 : return false;
1227 :
1228 : /* -------------------------------------------------------------------- */
1229 : /* Create in memory schema tree. */
1230 : /* -------------------------------------------------------------------- */
1231 : CPLXMLNode *psRoot =
1232 103 : CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClassList");
1233 :
1234 103 : if (m_nHasSequentialLayers != -1 && m_nClassCount > 1)
1235 : {
1236 18 : CPLCreateXMLElementAndValue(psRoot, "SequentialLayers",
1237 18 : m_nHasSequentialLayers ? "true" : "false");
1238 : }
1239 :
1240 230 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
1241 : {
1242 127 : CPLAddXMLChild(psRoot, m_papoClass[iClass]->SerializeToXML());
1243 : }
1244 :
1245 : /* -------------------------------------------------------------------- */
1246 : /* Serialize to disk. */
1247 : /* -------------------------------------------------------------------- */
1248 103 : char *pszWholeText = CPLSerializeXMLTree(psRoot);
1249 :
1250 103 : CPLDestroyXMLNode(psRoot);
1251 :
1252 103 : auto fp = VSIVirtualHandleUniquePtr(VSIFOpenL(pszFile, "wb"));
1253 :
1254 103 : bool bSuccess = true;
1255 103 : if (fp == nullptr)
1256 0 : bSuccess = false;
1257 : else
1258 : {
1259 103 : if (fp->Write(pszWholeText, strlen(pszWholeText), 1) != 1)
1260 0 : bSuccess = false;
1261 : }
1262 :
1263 103 : CPLFree(pszWholeText);
1264 :
1265 103 : return bSuccess;
1266 : }
1267 :
1268 : /************************************************************************/
1269 : /* PrescanForSchema() */
1270 : /* */
1271 : /* For now we use a pretty dumb approach of just doing a normal */
1272 : /* scan of the whole file, building up the schema information. */
1273 : /* Eventually we hope to do a more efficient scan when just */
1274 : /* looking for schema information. */
1275 : /************************************************************************/
1276 :
1277 122 : bool GMLReader::PrescanForSchema(bool bGetExtents, bool bOnlyDetectSRS)
1278 :
1279 : {
1280 122 : if (m_pszFilename == nullptr)
1281 0 : return false;
1282 :
1283 122 : if (!bOnlyDetectSRS)
1284 : {
1285 113 : SetClassListLocked(false);
1286 113 : ClearClasses();
1287 : }
1288 :
1289 122 : if (!SetupParser())
1290 0 : return false;
1291 :
1292 122 : m_bCanUseGlobalSRSName = true;
1293 :
1294 122 : GMLFeatureClass *poLastClass = nullptr;
1295 :
1296 122 : m_nHasSequentialLayers = TRUE;
1297 :
1298 122 : void *hCacheSRS = GML_BuildOGRGeometryFromList_CreateCache();
1299 :
1300 244 : std::string osWork;
1301 :
1302 131 : for (int i = 0; i < m_nClassCount; i++)
1303 : {
1304 9 : m_papoClass[i]->SetFeatureCount(-1);
1305 9 : m_papoClass[i]->SetSRSName(nullptr);
1306 : }
1307 :
1308 122 : GMLFeature *poFeature = nullptr;
1309 122 : std::set<GMLFeatureClass *> knownClasses;
1310 122 : bool bFoundPerFeatureSRSName = false;
1311 :
1312 842 : while ((poFeature = NextFeature()) != nullptr)
1313 : {
1314 720 : GMLFeatureClass *poClass = poFeature->GetClass();
1315 :
1316 720 : if (knownClasses.find(poClass) == knownClasses.end())
1317 : {
1318 141 : knownClasses.insert(poClass);
1319 141 : if (m_pszGlobalSRSName && GML_IsLegitSRSName(m_pszGlobalSRSName))
1320 : {
1321 46 : poClass->SetSRSName(m_pszGlobalSRSName);
1322 : }
1323 : }
1324 :
1325 755 : if (poLastClass != nullptr && poClass != poLastClass &&
1326 35 : poClass->GetFeatureCount() != -1)
1327 0 : m_nHasSequentialLayers = false;
1328 720 : poLastClass = poClass;
1329 :
1330 720 : if (poClass->GetFeatureCount() == -1)
1331 141 : poClass->SetFeatureCount(1);
1332 : else
1333 579 : poClass->SetFeatureCount(poClass->GetFeatureCount() + 1);
1334 :
1335 720 : const CPLXMLNode *const *papsGeometry = poFeature->GetGeometryList();
1336 720 : bool bGeometryColumnJustCreated = false;
1337 :
1338 720 : const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
1339 : const CPLXMLNode *psBoundedByGeometry =
1340 720 : poFeature->GetBoundedByGeometry();
1341 720 : int nFeatureGeomCount = poFeature->GetGeometryCount();
1342 720 : if (psBoundedByGeometry && nFeatureGeomCount == 0 &&
1343 3 : strcmp(psBoundedByGeometry->pszValue, "null") != 0)
1344 : {
1345 1 : apsGeometries[0] = psBoundedByGeometry;
1346 1 : papsGeometry = apsGeometries;
1347 1 : nFeatureGeomCount = 1;
1348 : }
1349 :
1350 720 : if (!bOnlyDetectSRS && papsGeometry != nullptr &&
1351 701 : papsGeometry[0] != nullptr)
1352 : {
1353 662 : if (poClass->GetGeometryPropertyCount() == 0)
1354 : {
1355 226 : std::string osPath(poClass->GetSingleGeomElemPath());
1356 141 : if (osPath.empty() &&
1357 141 : poClass->IsConsistentSingleGeomElemPath() &&
1358 23 : papsGeometry[0] == psBoundedByGeometry)
1359 : {
1360 1 : osPath = "boundedBy";
1361 : }
1362 113 : std::string osGeomName(osPath);
1363 113 : const auto nPos = osGeomName.rfind('|');
1364 113 : if (nPos != std::string::npos)
1365 1 : osGeomName = osGeomName.substr(nPos + 1);
1366 113 : bGeometryColumnJustCreated = true;
1367 226 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1368 226 : osGeomName.c_str(), osPath.c_str(), wkbUnknown, -1, true));
1369 : }
1370 : }
1371 :
1372 720 : if (bGetExtents && papsGeometry != nullptr)
1373 : {
1374 : const int nIters = std::min(nFeatureGeomCount,
1375 720 : poClass->GetGeometryPropertyCount());
1376 1409 : for (int i = 0; i < nIters; ++i)
1377 : {
1378 689 : if (papsGeometry[i] == nullptr)
1379 8 : continue;
1380 :
1381 684 : const CPLXMLNode *myGeometryList[2] = {papsGeometry[i],
1382 684 : nullptr};
1383 1368 : OGRGeometry *poGeometry = GML_BuildOGRGeometryFromList(
1384 684 : myGeometryList, true, m_bInvertAxisOrderIfLatLong, nullptr,
1385 684 : m_bConsiderEPSGAsURN, m_eSwapCoordinates,
1386 684 : m_bGetSecondaryGeometryOption, hCacheSRS,
1387 684 : m_bFaceHoleNegative);
1388 684 : if (poGeometry == nullptr)
1389 3 : continue;
1390 :
1391 681 : auto poGeomProperty = poClass->GetGeometryProperty(i);
1392 : OGRwkbGeometryType eGType =
1393 681 : static_cast<OGRwkbGeometryType>(poGeomProperty->GetType());
1394 :
1395 1362 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
1396 681 : myGeometryList, osWork, m_bConsiderEPSGAsURN);
1397 681 : if (pszSRSName != nullptr)
1398 143 : bFoundPerFeatureSRSName = true;
1399 :
1400 681 : if (pszSRSName != nullptr && m_pszGlobalSRSName != nullptr &&
1401 73 : !EQUAL(pszSRSName, m_pszGlobalSRSName))
1402 : {
1403 0 : m_bCanUseGlobalSRSName = false;
1404 : }
1405 681 : if (m_pszGlobalSRSName == nullptr || pszSRSName != nullptr)
1406 : {
1407 241 : if (poClass->GetGeometryPropertyCount() == 1)
1408 232 : poClass->MergeSRSName(pszSRSName);
1409 : else
1410 9 : poGeomProperty->MergeSRSName(pszSRSName ? pszSRSName
1411 : : "");
1412 : }
1413 :
1414 : // Merge geometry type into layer.
1415 681 : if (bGeometryColumnJustCreated)
1416 : {
1417 110 : poGeomProperty->SetType(poGeometry->getGeometryType());
1418 : }
1419 : else
1420 : {
1421 571 : poGeomProperty->SetType(OGRMergeGeometryTypesEx(
1422 571 : eGType, poGeometry->getGeometryType(), true));
1423 : }
1424 :
1425 : // Merge extents.
1426 681 : if (!poGeometry->IsEmpty())
1427 : {
1428 680 : double dfXMin = 0.0;
1429 680 : double dfXMax = 0.0;
1430 680 : double dfYMin = 0.0;
1431 680 : double dfYMax = 0.0;
1432 :
1433 680 : OGREnvelope sEnvelope;
1434 :
1435 680 : poGeometry->getEnvelope(&sEnvelope);
1436 680 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
1437 : {
1438 561 : dfXMin = std::min(dfXMin, sEnvelope.MinX);
1439 561 : dfXMax = std::max(dfXMax, sEnvelope.MaxX);
1440 561 : dfYMin = std::min(dfYMin, sEnvelope.MinY);
1441 561 : dfYMax = std::max(dfYMax, sEnvelope.MaxY);
1442 : }
1443 : else
1444 : {
1445 119 : dfXMin = sEnvelope.MinX;
1446 119 : dfXMax = sEnvelope.MaxX;
1447 119 : dfYMin = sEnvelope.MinY;
1448 119 : dfYMax = sEnvelope.MaxY;
1449 : }
1450 :
1451 680 : poClass->SetExtents(dfXMin, dfXMax, dfYMin, dfYMax);
1452 : }
1453 681 : delete poGeometry;
1454 : }
1455 : }
1456 :
1457 720 : delete poFeature;
1458 : }
1459 :
1460 122 : GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS);
1461 :
1462 122 : if (bGetExtents && m_bCanUseGlobalSRSName && m_pszGlobalSRSName &&
1463 30 : !bFoundPerFeatureSRSName && m_bInvertAxisOrderIfLatLong &&
1464 261 : GML_IsLegitSRSName(m_pszGlobalSRSName) &&
1465 17 : GML_IsSRSLatLongOrder(m_pszGlobalSRSName))
1466 : {
1467 : /* So when we have computed the extent, we didn't know yet */
1468 : /* the SRS to use. Now we know it, we have to fix the extent */
1469 : /* order */
1470 :
1471 6 : for (int i = 0; i < m_nClassCount; i++)
1472 : {
1473 3 : GMLFeatureClass *poClass = m_papoClass[i];
1474 3 : if (poClass->HasExtents())
1475 : {
1476 2 : double dfXMin = 0.0;
1477 2 : double dfXMax = 0.0;
1478 2 : double dfYMin = 0.0;
1479 2 : double dfYMax = 0.0;
1480 2 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
1481 2 : poClass->SetExtents(dfYMin, dfYMax, dfXMin, dfXMax);
1482 : }
1483 : }
1484 : }
1485 :
1486 122 : CleanupParser();
1487 :
1488 122 : return true;
1489 : }
1490 :
1491 : /************************************************************************/
1492 : /* ResetReading() */
1493 : /************************************************************************/
1494 :
1495 755 : void GMLReader::ResetReading()
1496 :
1497 : {
1498 755 : CleanupParser();
1499 755 : SetFilteredClassName(nullptr);
1500 755 : }
1501 :
1502 : /************************************************************************/
1503 : /* SetGlobalSRSName() */
1504 : /************************************************************************/
1505 :
1506 209 : void GMLReader::SetGlobalSRSName(const char *pszGlobalSRSName)
1507 : {
1508 209 : if (m_pszGlobalSRSName == nullptr && pszGlobalSRSName != nullptr)
1509 : {
1510 65 : const char *pszVertCS_EPSG = nullptr;
1511 79 : if (STARTS_WITH(pszGlobalSRSName, "EPSG:") &&
1512 14 : (pszVertCS_EPSG = strstr(pszGlobalSRSName, ", EPSG:")) != nullptr)
1513 : {
1514 2 : m_pszGlobalSRSName =
1515 2 : CPLStrdup(CPLSPrintf("EPSG:%d+%d", atoi(pszGlobalSRSName + 5),
1516 : atoi(pszVertCS_EPSG + 7)));
1517 : }
1518 63 : else if (STARTS_WITH(pszGlobalSRSName, "EPSG:") && m_bConsiderEPSGAsURN)
1519 : {
1520 5 : m_pszGlobalSRSName = CPLStrdup(
1521 : CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszGlobalSRSName + 5));
1522 : }
1523 : else
1524 : {
1525 58 : m_pszGlobalSRSName = CPLStrdup(pszGlobalSRSName);
1526 : }
1527 65 : m_bCanUseGlobalSRSName = m_pszGlobalSRSName != nullptr;
1528 : }
1529 209 : }
1530 :
1531 : /************************************************************************/
1532 : /* SetFilteredClassName() */
1533 : /************************************************************************/
1534 :
1535 833 : bool GMLReader::SetFilteredClassName(const char *pszClassName)
1536 : {
1537 833 : CPLFree(m_pszFilteredClassName);
1538 833 : m_pszFilteredClassName = pszClassName ? CPLStrdup(pszClassName) : nullptr;
1539 :
1540 833 : m_nFilteredClassIndex = -1;
1541 833 : if (m_pszFilteredClassName != nullptr)
1542 : {
1543 534 : for (int i = 0; i < m_nClassCount; i++)
1544 : {
1545 1002 : if (strcmp(m_papoClass[i]->GetElementName(),
1546 501 : m_pszFilteredClassName) == 0)
1547 : {
1548 45 : m_nFilteredClassIndex = i;
1549 45 : break;
1550 : }
1551 : }
1552 : }
1553 :
1554 833 : return true;
1555 : }
|