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