1 /******************************************************************************
2  * $Id: ogrgmldatasource.cpp 29330 2015-06-14 12:11:11Z rouault $
3  *
4  * Project:  OGR
5  * Purpose:  Implements OGRGMLDataSource class.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
10  * Copyright (c) 2007-2013, 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
23  * OR 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  * Contributor: Alessandro Furieri, a.furieri@lqt.it
32  * Portions of this module implenting GML_SKIP_RESOLVE_ELEMS HUGE
33  * Developed for Faunalia ( http://www.faunalia.it) with funding from
34  * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE
35  *
36  ****************************************************************************/
37 
38 #include "ogr_gml.h"
39 #include "parsexsd.h"
40 #include "cpl_conv.h"
41 #include "cpl_string.h"
42 #include "cpl_http.h"
43 #include "gmlutils.h"
44 #include "ogr_p.h"
45 #include "gmlregistry.h"
46 #include "gmlreaderp.h"
47 
48 #include <vector>
49 
50 CPL_CVSID("$Id: ogrgmldatasource.cpp 29330 2015-06-14 12:11:11Z rouault $");
51 
52 static int ExtractSRSName(const char* pszXML, char* szSRSName,
53                           size_t sizeof_szSRSName);
54 
55 /************************************************************************/
56 /*                   ReplaceSpaceByPct20IfNeeded()                      */
57 /************************************************************************/
58 
ReplaceSpaceByPct20IfNeeded(const char * pszURL)59 static CPLString ReplaceSpaceByPct20IfNeeded(const char* pszURL)
60 {
61     /* Replace ' ' by '%20' */
62     CPLString osRet = pszURL;
63     const char* pszNeedle = strstr(pszURL, "; ");
64     if (pszNeedle)
65     {
66         char* pszTmp = (char*)CPLMalloc(strlen(pszURL) + 2 +1);
67         int nBeforeNeedle = (int)(pszNeedle - pszURL);
68         memcpy(pszTmp, pszURL, nBeforeNeedle);
69         strcpy(pszTmp + nBeforeNeedle, ";%20");
70         strcpy(pszTmp + nBeforeNeedle + strlen(";%20"), pszNeedle + strlen("; "));
71         osRet = pszTmp;
72         CPLFree(pszTmp);
73     }
74 
75     return osRet;
76 }
77 
78 /************************************************************************/
79 /*                         OGRGMLDataSource()                         */
80 /************************************************************************/
81 
OGRGMLDataSource()82 OGRGMLDataSource::OGRGMLDataSource()
83 
84 {
85     pszName = NULL;
86     papoLayers = NULL;
87     nLayers = 0;
88 
89     poReader = NULL;
90     fpOutput = NULL;
91     bFpOutputIsNonSeekable = FALSE;
92     bFpOutputSingleFile = FALSE;
93     bIsOutputGML3 = FALSE;
94     bIsOutputGML3Deegree = FALSE;
95     bIsOutputGML32 = FALSE;
96     bIsLongSRSRequired = FALSE;
97     bWriteSpaceIndentation = TRUE;
98 
99     papszCreateOptions = NULL;
100     bOutIsTempFile = FALSE;
101 
102     bExposeGMLId = FALSE;
103     bExposeFid = FALSE;
104     nSchemaInsertLocation = -1;
105     nBoundedByLocation = -1;
106     bBBOX3D = FALSE;
107 
108     poWriteGlobalSRS = NULL;
109     bWriteGlobalSRS = FALSE;
110     bUseGlobalSRSName = FALSE;
111     bIsWFS = FALSE;
112 
113     eReadMode = STANDARD;
114     poStoredGMLFeature = NULL;
115     poLastReadLayer = NULL;
116 
117     m_bInvertAxisOrderIfLatLong = FALSE;
118     m_bConsiderEPSGAsURN = FALSE;
119     m_bGetSecondaryGeometryOption = FALSE;
120     bEmptyAsNull = TRUE;
121 }
122 
123 /************************************************************************/
124 /*                        ~OGRGMLDataSource()                         */
125 /************************************************************************/
126 
~OGRGMLDataSource()127 OGRGMLDataSource::~OGRGMLDataSource()
128 
129 {
130 
131     if( fpOutput != NULL )
132     {
133         if( nLayers == 0 )
134             WriteTopElements();
135 
136         const char* pszPrefix = GetAppPrefix();
137         if( RemoveAppPrefix() )
138             PrintLine( fpOutput, "</FeatureCollection>" );
139         else
140             PrintLine( fpOutput, "</%s:FeatureCollection>", pszPrefix );
141 
142         if( bFpOutputIsNonSeekable)
143         {
144             VSIFCloseL( fpOutput );
145             fpOutput = NULL;
146         }
147 
148         InsertHeader();
149 
150         if( !bFpOutputIsNonSeekable
151             && nBoundedByLocation != -1
152             && VSIFSeekL( fpOutput, nBoundedByLocation, SEEK_SET ) == 0 )
153         {
154             if (bWriteGlobalSRS && sBoundingRect.IsInit()  && IsGML3Output())
155             {
156                 int bCoordSwap = FALSE;
157                 char* pszSRSName;
158                 if (poWriteGlobalSRS)
159                     pszSRSName = GML_GetSRSName(poWriteGlobalSRS, IsLongSRSRequired(), &bCoordSwap);
160                 else
161                     pszSRSName = CPLStrdup("");
162                 char szLowerCorner[75], szUpperCorner[75];
163                 if (bCoordSwap)
164                 {
165                     OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinY, sBoundingRect.MinX, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
166                     OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxY, sBoundingRect.MaxX, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2);
167                 }
168                 else
169                 {
170                     OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinX, sBoundingRect.MinY, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
171                     OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxX, sBoundingRect.MaxY, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2);
172                 }
173                 if (bWriteSpaceIndentation)
174                     VSIFPrintfL( fpOutput, "  ");
175                 PrintLine( fpOutput, "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner></gml:Envelope></gml:boundedBy>",
176                            (bBBOX3D) ? " srsDimension=\"3\"" : "", pszSRSName, szLowerCorner, szUpperCorner);
177                 CPLFree(pszSRSName);
178             }
179             else if (bWriteGlobalSRS && sBoundingRect.IsInit())
180             {
181                 if (bWriteSpaceIndentation)
182                     VSIFPrintfL( fpOutput, "  ");
183                 PrintLine( fpOutput, "<gml:boundedBy>" );
184                 if (bWriteSpaceIndentation)
185                     VSIFPrintfL( fpOutput, "    ");
186                 PrintLine( fpOutput, "<gml:Box>" );
187                 if (bWriteSpaceIndentation)
188                     VSIFPrintfL( fpOutput, "      ");
189                 VSIFPrintfL( fpOutput,
190                             "<gml:coord><gml:X>%.16g</gml:X>"
191                             "<gml:Y>%.16g</gml:Y>",
192                             sBoundingRect.MinX, sBoundingRect.MinY );
193                 if (bBBOX3D)
194                     VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
195                                sBoundingRect.MinZ );
196                 PrintLine( fpOutput, "</gml:coord>");
197                 if (bWriteSpaceIndentation)
198                     VSIFPrintfL( fpOutput, "      ");
199                 VSIFPrintfL( fpOutput,
200                             "<gml:coord><gml:X>%.16g</gml:X>"
201                             "<gml:Y>%.16g</gml:Y>",
202                             sBoundingRect.MaxX, sBoundingRect.MaxY );
203                 if (bBBOX3D)
204                     VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
205                                sBoundingRect.MaxZ );
206                 PrintLine( fpOutput, "</gml:coord>");
207                 if (bWriteSpaceIndentation)
208                     VSIFPrintfL( fpOutput, "    ");
209                 PrintLine( fpOutput, "</gml:Box>" );
210                 if (bWriteSpaceIndentation)
211                     VSIFPrintfL( fpOutput, "  ");
212                 PrintLine( fpOutput, "</gml:boundedBy>" );
213             }
214             else
215             {
216                 if (bWriteSpaceIndentation)
217                     VSIFPrintfL( fpOutput, "  ");
218                 if (IsGML3Output())
219                     PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
220                 else
221                     PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
222             }
223         }
224 
225         if (fpOutput)
226             VSIFCloseL( fpOutput );
227     }
228 
229     CSLDestroy( papszCreateOptions );
230     CPLFree( pszName );
231 
232     for( int i = 0; i < nLayers; i++ )
233         delete papoLayers[i];
234 
235     CPLFree( papoLayers );
236 
237     if( poReader )
238     {
239         if (bOutIsTempFile)
240             VSIUnlink(poReader->GetSourceFileName());
241         delete poReader;
242     }
243 
244     delete poWriteGlobalSRS;
245 
246     delete poStoredGMLFeature;
247 
248     if (osXSDFilename.compare(CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this)) == 0)
249         VSIUnlink(osXSDFilename);
250 }
251 
252 /************************************************************************/
253 /*                            CheckHeader()                             */
254 /************************************************************************/
255 
CheckHeader(const char * pszStr)256 int OGRGMLDataSource::CheckHeader(const char* pszStr)
257 {
258     if( strstr(pszStr,"opengis.net/gml") == NULL &&
259         strstr(pszStr,"<csw:GetRecordsResponse") == NULL )
260     {
261         return FALSE;
262     }
263 
264     /* Ignore .xsd schemas */
265     if( strstr(pszStr, "<schema") != NULL
266         || strstr(pszStr, "<xs:schema") != NULL
267         || strstr(pszStr, "<xsd:schema") != NULL )
268     {
269         return FALSE;
270     }
271 
272     /* Ignore GeoRSS documents. They will be recognized by the GeoRSS driver */
273     if( strstr(pszStr, "<rss") != NULL && strstr(pszStr, "xmlns:georss") != NULL )
274     {
275         return FALSE;
276     }
277 
278     /* Ignore OpenJUMP .jml documents. They will be recognized by the OpenJUMP driver */
279     if( strstr(pszStr, "<JCSDataFile") != NULL )
280     {
281         return FALSE;
282     }
283 
284     /* Ignore OGR WFS xml description files, or WFS Capabilities results */
285     if( strstr(pszStr, "<OGRWFSDataSource>") != NULL ||
286         strstr(pszStr, "<wfs:WFS_Capabilities") != NULL )
287     {
288         return FALSE;
289     }
290 
291     /* Ignore WMTS capabilities results */
292     if( strstr(pszStr, "http://www.opengis.net/wmts/1.0") != NULL )
293     {
294         return FALSE;
295     }
296 
297     return TRUE;
298 }
299 
300 /************************************************************************/
301 /*                                Open()                                */
302 /************************************************************************/
303 
Open(GDALOpenInfo * poOpenInfo)304 int OGRGMLDataSource::Open( GDALOpenInfo* poOpenInfo )
305 
306 {
307     VSILFILE   *fp;
308     char        szHeader[4096];
309     GIntBig     nNumberOfFeatures = 0;
310     CPLString   osWithVsiGzip;
311     const char *pszSchemaLocation = NULL;
312     int bCheckAuxFile = TRUE;
313 
314 /* -------------------------------------------------------------------- */
315 /*      Extract xsd filename from connexion string if present.          */
316 /* -------------------------------------------------------------------- */
317     osFilename = poOpenInfo->pszFilename;
318     const char *pszXSDFilenameTmp = strstr(poOpenInfo->pszFilename, ",xsd=");
319     if (pszXSDFilenameTmp != NULL)
320     {
321         osFilename.resize(pszXSDFilenameTmp - poOpenInfo->pszFilename);
322         osXSDFilename = pszXSDFilenameTmp + strlen(",xsd=");
323     }
324     else
325         osXSDFilename = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "XSD", "");
326 
327     const char *pszFilename = osFilename.c_str();
328 
329     pszName = CPLStrdup( poOpenInfo->pszFilename );
330 
331 /* -------------------------------------------------------------------- */
332 /*      Open the source file.                                           */
333 /* -------------------------------------------------------------------- */
334     VSILFILE* fpToClose = NULL;
335     if( poOpenInfo->fpL != NULL )
336     {
337         fp = poOpenInfo->fpL;
338         VSIFSeekL(fp, 0, SEEK_SET);
339     }
340     else
341     {
342         fpToClose = fp = VSIFOpenL( pszFilename, "r" );
343         if( fp == NULL )
344             return FALSE;
345     }
346 
347     int bExpatCompatibleEncoding = FALSE;
348     int bHas3D = FALSE;
349     int bHintConsiderEPSGAsURN = FALSE;
350     int bAnalyzeSRSPerFeature = TRUE;
351 
352     char szSRSName[128];
353     szSRSName[0] = '\0';
354 
355 /* -------------------------------------------------------------------- */
356 /*      Load a header chunk and check for signs it is GML               */
357 /* -------------------------------------------------------------------- */
358 
359     size_t nRead = VSIFReadL( szHeader, 1, sizeof(szHeader)-1, fp );
360     if (nRead <= 0)
361     {
362         if( fpToClose )
363             VSIFCloseL( fpToClose );
364         return FALSE;
365     }
366     szHeader[nRead] = '\0';
367 
368     /* Might be a OS-Mastermap gzipped GML, so let be nice and try to open */
369     /* it transparently with /vsigzip/ */
370     if ( ((GByte*)szHeader)[0] == 0x1f && ((GByte*)szHeader)[1] == 0x8b &&
371             EQUAL(CPLGetExtension(pszFilename), "gz") &&
372             strncmp(pszFilename, "/vsigzip/", strlen("/vsigzip/")) != 0 )
373     {
374         if( fpToClose )
375             VSIFCloseL( fpToClose );
376         fpToClose = NULL;
377         osWithVsiGzip = "/vsigzip/";
378         osWithVsiGzip += pszFilename;
379 
380         pszFilename = osWithVsiGzip;
381 
382         fp = fpToClose = VSIFOpenL( pszFilename, "r" );
383         if( fp == NULL )
384             return FALSE;
385 
386         nRead = VSIFReadL( szHeader, 1, sizeof(szHeader) - 1, fp );
387         if (nRead <= 0)
388         {
389             VSIFCloseL( fpToClose );
390             return FALSE;
391         }
392         szHeader[nRead] = '\0';
393     }
394 
395 /* -------------------------------------------------------------------- */
396 /*      Check for a UTF-8 BOM and skip if found                         */
397 /*                                                                      */
398 /*      TODO: BOM is variable-lenght parameter and depends on encoding. */
399 /*            Add BOM detection for other encodings.                    */
400 /* -------------------------------------------------------------------- */
401 
402     // Used to skip to actual beginning of XML data
403     char* szPtr = szHeader;
404 
405     if( ( (unsigned char)szHeader[0] == 0xEF )
406         && ( (unsigned char)szHeader[1] == 0xBB )
407         && ( (unsigned char)szHeader[2] == 0xBF) )
408     {
409         szPtr += 3;
410     }
411 
412     const char* pszEncoding = strstr(szPtr, "encoding=");
413     if (pszEncoding)
414         bExpatCompatibleEncoding = (pszEncoding[9] == '\'' || pszEncoding[9] == '"') &&
415                                     (EQUALN(pszEncoding + 10, "UTF-8", 5) ||
416                                     EQUALN(pszEncoding + 10, "ISO-8859-15", 11) ||
417                                     (EQUALN(pszEncoding + 10, "ISO-8859-1", 10) &&
418                                         pszEncoding[20] == pszEncoding[9])) ;
419     else
420         bExpatCompatibleEncoding = TRUE; /* utf-8 is the default */
421 
422     bHas3D = strstr(szPtr, "srsDimension=\"3\"") != NULL || strstr(szPtr, "<gml:Z>") != NULL;
423 
424 /* -------------------------------------------------------------------- */
425 /*      Here, we expect the opening chevrons of GML tree root element   */
426 /* -------------------------------------------------------------------- */
427     if( szPtr[0] != '<' || !CheckHeader(szPtr) )
428     {
429         if( fpToClose )
430             VSIFCloseL( fpToClose );
431         return FALSE;
432     }
433 
434     /* Now we definitely own the file descriptor */
435     if( fp == poOpenInfo->fpL )
436         poOpenInfo->fpL = NULL;
437 
438     /* Small optimization: if we parse a <wfs:FeatureCollection>  and */
439     /* that numberOfFeatures is set, we can use it to set the FeatureCount */
440     /* but *ONLY* if there's just one class ! */
441     const char* pszFeatureCollection = strstr(szPtr, "wfs:FeatureCollection");
442     if (pszFeatureCollection == NULL)
443         pszFeatureCollection = strstr(szPtr, "gml:FeatureCollection"); /* GML 3.2.1 output */
444     if (pszFeatureCollection == NULL)
445     {
446         pszFeatureCollection = strstr(szPtr, "<FeatureCollection"); /* Deegree WFS 1.0.0 output */
447         if (pszFeatureCollection && strstr(szPtr, "xmlns:wfs=\"http://www.opengis.net/wfs\"") == NULL)
448             pszFeatureCollection = NULL;
449     }
450     if (pszFeatureCollection)
451     {
452         bExposeGMLId = TRUE;
453         bIsWFS = TRUE;
454         const char* pszNumberOfFeatures = strstr(szPtr, "numberOfFeatures=");
455         if (pszNumberOfFeatures)
456         {
457             pszNumberOfFeatures += 17;
458             char ch = pszNumberOfFeatures[0];
459             if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
460             {
461                 nNumberOfFeatures = CPLAtoGIntBig(pszNumberOfFeatures + 1);
462             }
463         }
464         else if ((pszNumberOfFeatures = strstr(szPtr, "numberReturned=")) != NULL) /* WFS 2.0.0 */
465         {
466             pszNumberOfFeatures += 15;
467             char ch = pszNumberOfFeatures[0];
468             if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
469             {
470                 /* 'unknown' might be a valid value in a corrected version of WFS 2.0 */
471                 /* but it will also evaluate to 0, that is considered as unknown, so nothing */
472                 /* particular to do */
473                 nNumberOfFeatures = CPLAtoGIntBig(pszNumberOfFeatures + 1);
474             }
475         }
476     }
477     else if (strncmp(pszFilename, "/vsimem/tempwfs_", strlen("/vsimem/tempwfs_")) == 0)
478     {
479         /* http://regis.intergraph.com/wfs/dcmetro/request.asp? returns a <G:FeatureCollection> */
480         /* Who knows what servers can return ? Ok, so when in the context of the WFS driver */
481         /* always expose the gml:id to avoid later crashes */
482         bExposeGMLId = TRUE;
483         bIsWFS = TRUE;
484     }
485     else
486     {
487         bExposeGMLId = strstr(szPtr, " gml:id=\"") != NULL ||
488                         strstr(szPtr, " gml:id='") != NULL;
489         bExposeFid = strstr(szPtr, " fid=\"") != NULL ||
490                         strstr(szPtr, " fid='") != NULL;
491     }
492 
493     const char* pszExposeGMLId = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
494         "EXPOSE_GML_ID", CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL));
495     if (pszExposeGMLId)
496         bExposeGMLId = CSLTestBoolean(pszExposeGMLId);
497 
498     const char* pszExposeFid = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
499         "EXPOSE_FID", CPLGetConfigOption("GML_EXPOSE_FID", NULL));
500     if (pszExposeFid)
501         bExposeFid = CSLTestBoolean(pszExposeFid);
502 
503     bHintConsiderEPSGAsURN = strstr(szPtr, "xmlns:fme=\"http://www.safe.com/gml/fme\"") != NULL;
504 
505     /* MTKGML */
506     if( strstr(szPtr, "<Maastotiedot") != NULL )
507     {
508         if( strstr(szPtr, "http://xml.nls.fi/XML/Namespace/Maastotietojarjestelma/SiirtotiedostonMalli/2011-02") == NULL )
509             CPLDebug("GML", "Warning: a MTKGML file was detected, but its namespace is unknown");
510         bAnalyzeSRSPerFeature = FALSE;
511         bUseGlobalSRSName = TRUE;
512         if( !ExtractSRSName(szPtr, szSRSName, sizeof(szSRSName)) )
513             strcpy(szSRSName, "EPSG:3067");
514     }
515 
516     pszSchemaLocation = strstr(szPtr, "schemaLocation=");
517     if (pszSchemaLocation)
518         pszSchemaLocation += strlen("schemaLocation=");
519 
520     if (strncmp(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")) == 0)
521         bCheckAuxFile = FALSE;
522     else if (strncmp(pszFilename, "/vsicurl/", strlen("/vsicurl/")) == 0 &&
523              (strstr(pszFilename, "?SERVICE=") || strstr(pszFilename, "&SERVICE=")) )
524         bCheckAuxFile = FALSE;
525 
526     int bIsWFSJointLayer = bIsWFS && strstr(szPtr, "<wfs:Tuple>");
527     if( bIsWFSJointLayer )
528         bExposeGMLId = FALSE;
529 
530 /* -------------------------------------------------------------------- */
531 /*      We assume now that it is GML.  Instantiate a GMLReader on it.   */
532 /* -------------------------------------------------------------------- */
533 
534     const char* pszReadMode = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
535             "READ_MODE",
536             CPLGetConfigOption("GML_READ_MODE", "AUTO"));
537     if( EQUAL(pszReadMode, "AUTO") )
538         pszReadMode = NULL;
539     if (pszReadMode == NULL || EQUAL(pszReadMode, "STANDARD"))
540         eReadMode = STANDARD;
541     else if (EQUAL(pszReadMode, "SEQUENTIAL_LAYERS"))
542         eReadMode = SEQUENTIAL_LAYERS;
543     else if (EQUAL(pszReadMode, "INTERLEAVED_LAYERS"))
544         eReadMode = INTERLEAVED_LAYERS;
545     else
546     {
547         CPLDebug("GML", "Unrecognized value for GML_READ_MODE configuration option.");
548     }
549 
550     m_bInvertAxisOrderIfLatLong =
551         CSLTestBoolean(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
552             "INVERT_AXIS_ORDER_IF_LAT_LONG",
553             CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES")));
554 
555     const char* pszConsiderEPSGAsURN =
556         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
557             "CONSIDER_EPSG_AS_URN",
558             CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", "AUTO"));
559     if( !EQUAL(pszConsiderEPSGAsURN, "AUTO") )
560         m_bConsiderEPSGAsURN = CSLTestBoolean(pszConsiderEPSGAsURN);
561     else if (bHintConsiderEPSGAsURN)
562     {
563         /* GML produced by FME (at least CanVec GML) seem to honour EPSG axis ordering */
564         CPLDebug("GML", "FME-produced GML --> consider that GML_CONSIDER_EPSG_AS_URN is set to YES");
565         m_bConsiderEPSGAsURN = TRUE;
566     }
567     else
568         m_bConsiderEPSGAsURN = FALSE;
569 
570     m_bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO"));
571 
572     /* EXPAT is faster than Xerces, so when it is safe to use it, use it ! */
573     /* The only interest of Xerces is for rare encodings that Expat doesn't handle */
574     /* but UTF-8 is well handled by Expat */
575     int bUseExpatParserPreferably = bExpatCompatibleEncoding;
576 
577     /* Override default choice */
578     const char* pszGMLParser = CPLGetConfigOption("GML_PARSER", NULL);
579     if (pszGMLParser)
580     {
581         if (EQUAL(pszGMLParser, "EXPAT"))
582             bUseExpatParserPreferably = TRUE;
583         else if (EQUAL(pszGMLParser, "XERCES"))
584             bUseExpatParserPreferably = FALSE;
585     }
586 
587     poReader = CreateGMLReader( bUseExpatParserPreferably,
588                                 m_bInvertAxisOrderIfLatLong,
589                                 m_bConsiderEPSGAsURN,
590                                 m_bGetSecondaryGeometryOption );
591     if( poReader == NULL )
592     {
593         CPLError( CE_Failure, CPLE_AppDefined,
594                   "File %s appears to be GML but the GML reader can't\n"
595                   "be instantiated, likely because Xerces or Expat support wasn't\n"
596                   "configured in.",
597                   pszFilename );
598         VSIFCloseL( fp );
599         return FALSE;
600     }
601 
602     poReader->SetSourceFile( pszFilename );
603     ((GMLReader*)poReader)->SetIsWFSJointLayer(bIsWFSJointLayer);
604     bEmptyAsNull = CSLFetchBoolean(poOpenInfo->papszOpenOptions, "EMPTY_AS_NULL", TRUE);
605     ((GMLReader*)poReader)->SetEmptyAsNull(bEmptyAsNull);
606     ((GMLReader*)poReader)->SetReportAllAttributes(
607         CSLFetchBoolean(poOpenInfo->papszOpenOptions, "GML_ATTRIBUTES_TO_OGR_FIELDS",
608             CSLTestBoolean(CPLGetConfigOption("GML_ATTRIBUTES_TO_OGR_FIELDS", "NO"))));
609 
610 /* -------------------------------------------------------------------- */
611 /*      Find <gml:description>, <gml:name> and <gml:boundedBy>          */
612 /* -------------------------------------------------------------------- */
613 
614     FindAndParseTopElements(fp);
615 
616     if( szSRSName[0] != '\0' )
617         poReader->SetGlobalSRSName(szSRSName);
618 
619 /* -------------------------------------------------------------------- */
620 /*      Resolve the xlinks in the source file and save it with the      */
621 /*      extension ".resolved.gml". The source file will to set to that. */
622 /* -------------------------------------------------------------------- */
623 
624     char *pszXlinkResolvedFilename = NULL;
625     const char *pszOption = CPLGetConfigOption("GML_SAVE_RESOLVED_TO", NULL);
626     int bResolve = TRUE;
627     int bHugeFile = FALSE;
628     if( pszOption != NULL && EQUALN( pszOption, "SAME", 4 ) )
629     {
630         // "SAME" will overwrite the existing gml file
631         pszXlinkResolvedFilename = CPLStrdup( pszFilename );
632     }
633     else if( pszOption != NULL &&
634              CPLStrnlen( pszOption, 5 ) >= 5 &&
635              EQUALN( pszOption - 4 + strlen( pszOption ), ".gml", 4 ) )
636     {
637         // Any string ending with ".gml" will try and write to it
638         pszXlinkResolvedFilename = CPLStrdup( pszOption );
639     }
640     else
641     {
642         // When no option is given or is not recognised,
643         // use the same file name with the extension changed to .resolved.gml
644         pszXlinkResolvedFilename = CPLStrdup(
645                             CPLResetExtension( pszFilename, "resolved.gml" ) );
646 
647         // Check if the file already exists.
648         VSIStatBufL sResStatBuf, sGMLStatBuf;
649         if( bCheckAuxFile && VSIStatL( pszXlinkResolvedFilename, &sResStatBuf ) == 0 )
650         {
651             VSIStatL( pszFilename, &sGMLStatBuf );
652             if( sGMLStatBuf.st_mtime > sResStatBuf.st_mtime )
653             {
654                 CPLDebug( "GML",
655                           "Found %s but ignoring because it appears\n"
656                           "be older than the associated GML file.",
657                           pszXlinkResolvedFilename );
658             }
659             else
660             {
661                 poReader->SetSourceFile( pszXlinkResolvedFilename );
662                 bResolve = FALSE;
663             }
664         }
665     }
666 
667     const char *pszSkipOption = CPLGetConfigOption( "GML_SKIP_RESOLVE_ELEMS",
668                                                     "ALL");
669     char **papszSkip = NULL;
670     if( EQUAL( pszSkipOption, "ALL" ) )
671         bResolve = FALSE;
672     else if( EQUAL( pszSkipOption, "HUGE" ) )//exactly as NONE, but intended for HUGE files
673         bHugeFile = TRUE;
674     else if( !EQUAL( pszSkipOption, "NONE" ) )//use this to resolve everything
675         papszSkip = CSLTokenizeString2( pszSkipOption, ",",
676                                            CSLT_STRIPLEADSPACES |
677                                            CSLT_STRIPENDSPACES );
678     int         bHaveSchema = FALSE;
679     int         bSchemaDone = FALSE;
680 
681 /* -------------------------------------------------------------------- */
682 /*      Is some GML Feature Schema (.gfs) TEMPLATE required ?           */
683 /* -------------------------------------------------------------------- */
684     const char *pszGFSTemplateName =
685         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "GFS_TEMPLATE",
686                 CPLGetConfigOption( "GML_GFS_TEMPLATE", NULL));
687     if( pszGFSTemplateName != NULL )
688     {
689         /* attempting to load the GFS TEMPLATE */
690         bHaveSchema = poReader->LoadClasses( pszGFSTemplateName );
691     }
692 
693     if( bResolve )
694     {
695         if ( bHugeFile )
696         {
697             bSchemaDone = TRUE;
698             int bSqliteIsTempFile =
699                 CSLTestBoolean(CPLGetConfigOption( "GML_HUGE_TEMPFILE", "YES"));
700             int iSqliteCacheMB = atoi(CPLGetConfigOption( "OGR_SQLITE_CACHE", "0"));
701             if( poReader->HugeFileResolver( pszXlinkResolvedFilename,
702                                             bSqliteIsTempFile,
703                                             iSqliteCacheMB ) == FALSE )
704             {
705                 // we assume an errors have been reported.
706                 VSIFCloseL(fp);
707                 CPLFree( pszXlinkResolvedFilename );
708                 return FALSE;
709             }
710         }
711         else
712         {
713             poReader->ResolveXlinks( pszXlinkResolvedFilename,
714                                      &bOutIsTempFile,
715                                      papszSkip );
716         }
717     }
718 
719     CPLFree( pszXlinkResolvedFilename );
720     pszXlinkResolvedFilename = NULL;
721     CSLDestroy( papszSkip );
722     papszSkip = NULL;
723 
724     /* If the source filename for the reader is still the GML filename, then */
725     /* we can directly provide the file pointer. Otherwise we close it */
726     if (strcmp(poReader->GetSourceFileName(), pszFilename) == 0)
727         poReader->SetFP(fp);
728     else
729         VSIFCloseL(fp);
730     fp = NULL;
731 
732     /* Is a prescan required ? */
733     if( bHaveSchema && !bSchemaDone )
734     {
735         /* We must detect which layers are actually present in the .gml */
736         /* and how many features they have */
737         if( !poReader->PrescanForTemplate() )
738         {
739             // we assume an errors have been reported.
740             return FALSE;
741         }
742     }
743 
744     CPLString osGFSFilename = CPLResetExtension( pszFilename, "gfs" );
745     if (strncmp(osGFSFilename, "/vsigzip/", strlen("/vsigzip/")) == 0)
746         osGFSFilename = osGFSFilename.substr(strlen("/vsigzip/"));
747 
748 /* -------------------------------------------------------------------- */
749 /*      Can we find a GML Feature Schema (.gfs) for the input file?     */
750 /* -------------------------------------------------------------------- */
751     if( !bHaveSchema && osXSDFilename.size() == 0)
752     {
753         VSIStatBufL sGFSStatBuf;
754         if( bCheckAuxFile && VSIStatL( osGFSFilename, &sGFSStatBuf ) == 0 )
755         {
756             VSIStatBufL sGMLStatBuf;
757             VSIStatL( pszFilename, &sGMLStatBuf );
758             if( sGMLStatBuf.st_mtime > sGFSStatBuf.st_mtime )
759             {
760                 CPLDebug( "GML",
761                           "Found %s but ignoring because it appears\n"
762                           "be older than the associated GML file.",
763                           osGFSFilename.c_str() );
764             }
765             else
766             {
767                 bHaveSchema = poReader->LoadClasses( osGFSFilename );
768                 if (bHaveSchema)
769                 {
770                     const char *pszXSDFilenameTmp;
771                     pszXSDFilenameTmp = CPLResetExtension( pszFilename, "xsd" );
772                     if( VSIStatExL( pszXSDFilenameTmp, &sGMLStatBuf,
773                                     VSI_STAT_EXISTS_FLAG ) == 0 )
774                     {
775                         CPLDebug("GML", "Using %s file, ignoring %s",
776                                  osGFSFilename.c_str(), pszXSDFilenameTmp);
777                     }
778                 }
779             }
780         }
781     }
782 
783 /* -------------------------------------------------------------------- */
784 /*      Can we find an xsd which might conform to tbe GML3 Level 0      */
785 /*      profile?  We really ought to look for it based on the rules     */
786 /*      schemaLocation in the GML feature collection but for now we     */
787 /*      just hopes it is in the same director with the same name.       */
788 /* -------------------------------------------------------------------- */
789     int bHasFoundXSD = FALSE;
790 
791     if( !bHaveSchema )
792     {
793         char** papszTypeNames = NULL;
794 
795         VSIStatBufL sXSDStatBuf;
796         if (osXSDFilename.size() == 0)
797         {
798             osXSDFilename = CPLResetExtension( pszFilename, "xsd" );
799             if( bCheckAuxFile && VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
800             {
801                 bHasFoundXSD = TRUE;
802             }
803         }
804         else
805         {
806             if ( strncmp(osXSDFilename, "http://", 7) == 0 ||
807                  strncmp(osXSDFilename, "https://", 8) == 0 ||
808                  VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
809             {
810                 bHasFoundXSD = TRUE;
811             }
812         }
813 
814         /* If not found, try if there is a schema in the gml_registry.xml */
815         /* that might match a declared namespace and featuretype */
816         if( !bHasFoundXSD )
817         {
818             GMLRegistry oRegistry(CSLFetchNameValueDef(
819                     poOpenInfo->papszOpenOptions, "REGISTRY",
820                                     CPLGetConfigOption("GML_REGISTRY", "")));
821             if( oRegistry.Parse() )
822             {
823                 CPLString osHeader(szHeader);
824                 for( size_t iNS = 0; iNS < oRegistry.aoNamespaces.size(); iNS ++ )
825                 {
826                     GMLRegistryNamespace& oNamespace = oRegistry.aoNamespaces[iNS];
827                     const char* pszNSToFind =
828                                 CPLSPrintf("xmlns:%s", oNamespace.osPrefix.c_str());
829                     const char* pszURIToFind =
830                                 CPLSPrintf("\"%s\"", oNamespace.osURI.c_str());
831                     /* Case sensitive comparison since below test that also */
832                     /* uses the namespace prefix is case sensitive */
833                     if( osHeader.find(pszNSToFind) != std::string::npos &&
834                         strstr(szHeader, pszURIToFind) != NULL )
835                     {
836                         if( oNamespace.bUseGlobalSRSName )
837                             bUseGlobalSRSName = TRUE;
838 
839                         for( size_t iTypename = 0;
840                                     iTypename < oNamespace.aoFeatureTypes.size();
841                                     iTypename ++ )
842                         {
843                             const char* pszElementToFind = NULL;
844 
845                             GMLRegistryFeatureType& oFeatureType =
846                                         oNamespace.aoFeatureTypes[iTypename];
847 
848                             if ( oFeatureType.osElementValue.size() )
849                                 pszElementToFind = CPLSPrintf("%s:%s>%s",
850                                                               oNamespace.osPrefix.c_str(),
851                                                               oFeatureType.osElementName.c_str(),
852                                                               oFeatureType.osElementValue.c_str());
853                             else
854                                 pszElementToFind = CPLSPrintf("%s:%s",
855                                                               oNamespace.osPrefix.c_str(),
856                                                               oFeatureType.osElementName.c_str());
857 
858                             /* Case sensitive test since in a CadastralParcel feature */
859                             /* there is a property basicPropertyUnit xlink, not to be */
860                             /* confused with a top-level BasicPropertyUnit feature... */
861                             if( osHeader.find(pszElementToFind) != std::string::npos )
862                             {
863                                 if( oFeatureType.osSchemaLocation.size() )
864                                 {
865                                     osXSDFilename = oFeatureType.osSchemaLocation;
866                                     if( strncmp(osXSDFilename, "http://", 7) == 0 ||
867                                         strncmp(osXSDFilename, "https://", 8) == 0 ||
868                                         VSIStatExL( osXSDFilename, &sXSDStatBuf,
869                                                     VSI_STAT_EXISTS_FLAG ) == 0 )
870                                     {
871                                         bHasFoundXSD = TRUE;
872                                         bHaveSchema = TRUE;
873                                         CPLDebug("GML", "Found %s for %s:%s in registry",
874                                                 osXSDFilename.c_str(),
875                                                 oNamespace.osPrefix.c_str(),
876                                                 oFeatureType.osElementName.c_str());
877                                     }
878                                     else
879                                     {
880                                         CPLDebug("GML", "Cannot open %s", osXSDFilename.c_str());
881                                     }
882                                 }
883                                 else
884                                 {
885                                     bHaveSchema = poReader->LoadClasses(
886                                         oFeatureType.osGFSSchemaLocation );
887                                     if( bHaveSchema )
888                                     {
889                                         CPLDebug("GML", "Found %s for %s:%s in registry",
890                                                 oFeatureType.osGFSSchemaLocation.c_str(),
891                                                 oNamespace.osPrefix.c_str(),
892                                                 oFeatureType.osElementName.c_str());
893                                     }
894                                 }
895                                 break;
896                             }
897                         }
898                         break;
899                     }
900                 }
901             }
902         }
903 
904         /* For WFS, try to fetch the application schema */
905         if( bIsWFS && !bHaveSchema && pszSchemaLocation != NULL &&
906             (pszSchemaLocation[0] == '\'' || pszSchemaLocation[0] == '"') &&
907              strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) != NULL )
908         {
909             char* pszSchemaLocationTmp1 = CPLStrdup(pszSchemaLocation + 1);
910             int nTruncLen = (int)(strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) - (pszSchemaLocation + 1));
911             pszSchemaLocationTmp1[nTruncLen] = '\0';
912             char* pszSchemaLocationTmp2 = CPLUnescapeString(
913                 pszSchemaLocationTmp1, NULL, CPLES_XML);
914             CPLString osEscaped = ReplaceSpaceByPct20IfNeeded(pszSchemaLocationTmp2);
915             CPLFree(pszSchemaLocationTmp2);
916             pszSchemaLocationTmp2 = CPLStrdup(osEscaped);
917             if (pszSchemaLocationTmp2)
918             {
919                 /* pszSchemaLocationTmp2 is of the form : */
920                 /* http://namespace1 http://namespace1_schema_location http://namespace2 http://namespace1_schema_location2 */
921                 /* So we try to find http://namespace1_schema_location that contains hints that it is the WFS application */
922                 /* schema, i.e. if it contains typename= and request=DescribeFeatureType */
923                 char** papszTokens = CSLTokenizeString2(pszSchemaLocationTmp2, " \r\n", 0);
924                 int nTokens = CSLCount(papszTokens);
925                 if ((nTokens % 2) == 0)
926                 {
927                     for(int i=0;i<nTokens;i+=2)
928                     {
929                         const char* pszEscapedURL = papszTokens[i+1];
930                         char* pszLocation = CPLUnescapeString(pszEscapedURL, NULL, CPLES_URL);
931                         CPLString osLocation = pszLocation;
932                         CPLFree(pszLocation);
933                         if (osLocation.ifind("typename=") != std::string::npos &&
934                             osLocation.ifind("request=DescribeFeatureType") != std::string::npos)
935                         {
936                             CPLString osTypeName = CPLURLGetValue(osLocation, "typename");
937                             papszTypeNames = CSLTokenizeString2( osTypeName, ",", 0);
938 
939                             if (!bHasFoundXSD && CPLHTTPEnabled() &&
940                                 CSLFetchBoolean(poOpenInfo->papszOpenOptions,
941                                     "DOWNLOAD_SCHEMA",
942                                     CSLTestBoolean(CPLGetConfigOption("GML_DOWNLOAD_WFS_SCHEMA", "YES"))) )
943                             {
944                                 CPLHTTPResult* psResult = CPLHTTPFetch(pszEscapedURL, NULL);
945                                 if (psResult)
946                                 {
947                                     if (psResult->nStatus == 0 && psResult->pabyData != NULL)
948                                     {
949                                         bHasFoundXSD = TRUE;
950                                         osXSDFilename =
951                                             CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this);
952                                         VSILFILE* fpMem = VSIFileFromMemBuffer(
953                                             osXSDFilename, psResult->pabyData,
954                                             psResult->nDataLen, TRUE);
955                                         VSIFCloseL(fpMem);
956                                         psResult->pabyData = NULL;
957                                     }
958                                     CPLHTTPDestroyResult(psResult);
959                                 }
960                             }
961                             break;
962                         }
963                     }
964                 }
965                 CSLDestroy(papszTokens);
966             }
967             CPLFree(pszSchemaLocationTmp2);
968             CPLFree(pszSchemaLocationTmp1);
969         }
970 
971         int bHasFeatureProperties = FALSE;
972         if( bHasFoundXSD )
973         {
974             std::vector<GMLFeatureClass*> aosClasses;
975             int bFullyUnderstood = FALSE;
976             bHaveSchema = GMLParseXSD( osXSDFilename, aosClasses, bFullyUnderstood );
977 
978             if( bHaveSchema && !bFullyUnderstood && bIsWFSJointLayer )
979             {
980                 CPLDebug("GML", "Schema found, but only partially understood. Cannot be used in a WFS join context");
981 
982                 std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
983                 std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
984                 while (iter != eiter)
985                 {
986                     GMLFeatureClass* poClass = *iter;
987 
988                     delete poClass;
989                     iter ++;
990                 }
991                 aosClasses.resize(0);
992                 bHaveSchema = FALSE;
993             }
994 
995             if( bHaveSchema )
996             {
997                 CPLDebug("GML", "Using %s", osXSDFilename.c_str());
998                 std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
999                 std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
1000                 while (iter != eiter)
1001                 {
1002                     GMLFeatureClass* poClass = *iter;
1003 
1004                     if( poClass->HasFeatureProperties() )
1005                     {
1006                         bHasFeatureProperties = TRUE;
1007                         break;
1008                     }
1009                     iter ++;
1010                 }
1011 
1012                 iter = aosClasses.begin();
1013                 while (iter != eiter)
1014                 {
1015                     GMLFeatureClass* poClass = *iter;
1016                     iter ++;
1017 
1018                     /* We have no way of knowing if the geometry type is 25D */
1019                     /* when examining the xsd only, so if there was a hint */
1020                     /* it is, we force to 25D */
1021                     if (bHas3D && poClass->GetGeometryPropertyCount() == 1)
1022                     {
1023                         poClass->GetGeometryProperty(0)->SetType(
1024                             wkbSetZ((OGRwkbGeometryType)poClass->GetGeometryProperty(0)->GetType()));
1025                     }
1026 
1027                     int bAddClass = TRUE;
1028                     /* If typenames are declared, only register the matching classes, in case */
1029                     /* the XSD contains more layers, but not if feature classes contain */
1030                     /* feature properties, in which case we will have embedded features that */
1031                     /* will be reported as top-level features */
1032                     if( papszTypeNames != NULL && !bHasFeatureProperties )
1033                     {
1034                         bAddClass = FALSE;
1035                         char** papszIter = papszTypeNames;
1036                         while (*papszIter && !bAddClass)
1037                         {
1038                             const char* pszTypeName = *papszIter;
1039                             if (strcmp(pszTypeName, poClass->GetName()) == 0)
1040                                 bAddClass = TRUE;
1041                             papszIter ++;
1042                         }
1043 
1044                         /* Retry by removing prefixes */
1045                         if (!bAddClass)
1046                         {
1047                             papszIter = papszTypeNames;
1048                             while (*papszIter && !bAddClass)
1049                             {
1050                                 const char* pszTypeName = *papszIter;
1051                                 const char* pszColon = strchr(pszTypeName, ':');
1052                                 if (pszColon)
1053                                 {
1054                                     pszTypeName = pszColon + 1;
1055                                     if (strcmp(pszTypeName, poClass->GetName()) == 0)
1056                                     {
1057                                         poClass->SetName(pszTypeName);
1058                                         bAddClass = TRUE;
1059                                     }
1060                                 }
1061                                 papszIter ++;
1062                             }
1063                         }
1064 
1065                     }
1066 
1067                     if (bAddClass)
1068                         poReader->AddClass( poClass );
1069                     else
1070                         delete poClass;
1071                 }
1072 
1073                 poReader->SetClassListLocked( TRUE );
1074             }
1075         }
1076 
1077         if (bHaveSchema && bIsWFS)
1078         {
1079             if( bIsWFSJointLayer )
1080             {
1081                 BuildJointClassFromXSD();
1082             }
1083 
1084             /* For WFS, we can assume sequential layers */
1085             if (poReader->GetClassCount() > 1 && pszReadMode == NULL &&
1086                 !bHasFeatureProperties)
1087             {
1088                 CPLDebug("GML", "WFS output. Using SEQUENTIAL_LAYERS read mode");
1089                 eReadMode = SEQUENTIAL_LAYERS;
1090             }
1091             /* Sometimes the returned schema contains only <xs:include> that we don't resolve */
1092             /* so ignore it */
1093             else if (poReader->GetClassCount() == 0)
1094                 bHaveSchema = FALSE;
1095         }
1096 
1097         CSLDestroy(papszTypeNames);
1098     }
1099 
1100 /* -------------------------------------------------------------------- */
1101 /*      Force a first pass to establish the schema.  Eventually we      */
1102 /*      will have mechanisms for remembering the schema and related     */
1103 /*      information.                                                    */
1104 /* -------------------------------------------------------------------- */
1105     if( !bHaveSchema ||
1106         CSLFetchBoolean(poOpenInfo->papszOpenOptions, "FORCE_SRS_DETECTION", FALSE) )
1107     {
1108         int bOnlyDetectSRS = bHaveSchema;
1109         if( !poReader->PrescanForSchema( TRUE, bAnalyzeSRSPerFeature,
1110                                          bOnlyDetectSRS ) )
1111         {
1112             // we assume an errors have been reported.
1113             return FALSE;
1114         }
1115         if( !bHaveSchema )
1116         {
1117             if( bIsWFSJointLayer && poReader->GetClassCount() == 1 )
1118             {
1119                 BuildJointClassFromScannedSchema();
1120             }
1121 
1122             if( bHasFoundXSD )
1123             {
1124                 CPLDebug("GML", "Generating %s file, ignoring %s",
1125                         osGFSFilename.c_str(), osXSDFilename.c_str());
1126             }
1127         }
1128     }
1129 
1130     if (poReader->GetClassCount() > 1 && poReader->IsSequentialLayers() &&
1131         pszReadMode == NULL)
1132     {
1133         CPLDebug("GML", "Layers are monoblock. Using SEQUENTIAL_LAYERS read mode");
1134         eReadMode = SEQUENTIAL_LAYERS;
1135     }
1136 
1137 /* -------------------------------------------------------------------- */
1138 /*      Save the schema file if possible.  Don't make a fuss if we      */
1139 /*      can't ... could be read-only directory or something.            */
1140 /* -------------------------------------------------------------------- */
1141     if( !bHaveSchema && !poReader->HasStoppedParsing() &&
1142         !EQUALN(pszFilename, "/vsitar/", strlen("/vsitar/")) &&
1143         !EQUALN(pszFilename, "/vsizip/", strlen("/vsizip/")) &&
1144         !EQUALN(pszFilename, "/vsigzip/vsi", strlen("/vsigzip/vsi")) &&
1145         !EQUALN(pszFilename, "/vsigzip//vsi", strlen("/vsigzip//vsi")) &&
1146         !EQUALN(pszFilename, "/vsicurl/", strlen("/vsicurl/")) &&
1147         !EQUALN(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")))
1148     {
1149         VSILFILE    *fp = NULL;
1150 
1151         VSIStatBufL sGFSStatBuf;
1152         if( VSIStatExL( osGFSFilename, &sGFSStatBuf, VSI_STAT_EXISTS_FLAG ) != 0
1153             && (fp = VSIFOpenL( osGFSFilename, "wt" )) != NULL )
1154         {
1155             VSIFCloseL( fp );
1156             poReader->SaveClasses( osGFSFilename );
1157         }
1158         else
1159         {
1160             CPLDebug("GML",
1161                      "Not saving %s files already exists or can't be created.",
1162                      osGFSFilename.c_str() );
1163         }
1164     }
1165 
1166 /* -------------------------------------------------------------------- */
1167 /*      Translate the GMLFeatureClasses into layers.                    */
1168 /* -------------------------------------------------------------------- */
1169     papoLayers = (OGRGMLLayer **)
1170         CPLCalloc( sizeof(OGRGMLLayer *), poReader->GetClassCount());
1171     nLayers = 0;
1172 
1173     if (poReader->GetClassCount() == 1 && nNumberOfFeatures != 0)
1174     {
1175         GMLFeatureClass *poClass = poReader->GetClass(0);
1176         GIntBig nFeatureCount = poClass->GetFeatureCount();
1177         if (nFeatureCount < 0)
1178         {
1179             poClass->SetFeatureCount(nNumberOfFeatures);
1180         }
1181         else if (nFeatureCount != nNumberOfFeatures)
1182         {
1183             CPLDebug("GML", "Feature count in header, and actual feature count don't match");
1184         }
1185     }
1186 
1187     if (bIsWFS && poReader->GetClassCount() == 1)
1188         bUseGlobalSRSName = TRUE;
1189 
1190     while( nLayers < poReader->GetClassCount() )
1191     {
1192         papoLayers[nLayers] = TranslateGMLSchema(poReader->GetClass(nLayers));
1193         nLayers++;
1194     }
1195 
1196 
1197 
1198     return TRUE;
1199 }
1200 
1201 /************************************************************************/
1202 /*                          BuildJointClassFromXSD()                    */
1203 /************************************************************************/
1204 
BuildJointClassFromXSD()1205 void OGRGMLDataSource::BuildJointClassFromXSD()
1206 {
1207     CPLString osJointClassName = "join";
1208     for(int i=0;i<poReader->GetClassCount();i++)
1209     {
1210         osJointClassName += "_";
1211         osJointClassName += poReader->GetClass(i)->GetName();
1212     }
1213     GMLFeatureClass* poJointClass = new GMLFeatureClass(osJointClassName);
1214     poJointClass->SetElementName("Tuple");
1215     for(int i=0;i<poReader->GetClassCount();i++)
1216     {
1217         GMLFeatureClass* poClass = poReader->GetClass(i);
1218 
1219         CPLString osPropertyName;
1220         osPropertyName.Printf("%s.%s", poClass->GetName(), "gml_id");
1221         GMLPropertyDefn* poNewProperty = new GMLPropertyDefn( osPropertyName );
1222         CPLString osSrcElement;
1223             osSrcElement.Printf("member|%s@id",
1224                                 poClass->GetName());
1225         poNewProperty->SetSrcElement(osSrcElement);
1226         poNewProperty->SetType(GMLPT_String);
1227         poJointClass->AddProperty(poNewProperty);
1228 
1229         int iField;
1230         for( iField = 0; iField < poClass->GetPropertyCount(); iField++ )
1231         {
1232             GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
1233             CPLString osPropertyName;
1234             osPropertyName.Printf("%s.%s", poClass->GetName(), poProperty->GetName());
1235             GMLPropertyDefn* poNewProperty = new GMLPropertyDefn( osPropertyName );
1236 
1237             poNewProperty->SetType(poProperty->GetType());
1238             CPLString osSrcElement;
1239             osSrcElement.Printf("member|%s|%s",
1240                                 poClass->GetName(),
1241                                 poProperty->GetSrcElement());
1242             poNewProperty->SetSrcElement(osSrcElement);
1243             poNewProperty->SetWidth(poProperty->GetWidth());
1244             poNewProperty->SetPrecision(poProperty->GetPrecision());
1245             poNewProperty->SetNullable(poProperty->IsNullable());
1246 
1247             poJointClass->AddProperty(poNewProperty);
1248         }
1249         for( iField = 0; iField < poClass->GetGeometryPropertyCount(); iField++ )
1250         {
1251             GMLGeometryPropertyDefn *poProperty = poClass->GetGeometryProperty( iField );
1252             CPLString osPropertyName;
1253             osPropertyName.Printf("%s.%s", poClass->GetName(), poProperty->GetName());
1254             CPLString osSrcElement;
1255             osSrcElement.Printf("member|%s|%s",
1256                                 poClass->GetName(),
1257                                 poProperty->GetSrcElement());
1258             GMLGeometryPropertyDefn* poNewProperty =
1259                 new GMLGeometryPropertyDefn( osPropertyName, osSrcElement,
1260                         poProperty->GetType(), -1, poProperty->IsNullable() );
1261             poJointClass->AddGeometryProperty(poNewProperty);
1262         }
1263     }
1264     poJointClass->SetSchemaLocked(TRUE);
1265 
1266     poReader->ClearClasses();
1267     poReader->AddClass( poJointClass );
1268 }
1269 
1270 /************************************************************************/
1271 /*                   BuildJointClassFromScannedSchema()                 */
1272 /************************************************************************/
1273 
BuildJointClassFromScannedSchema()1274 void OGRGMLDataSource::BuildJointClassFromScannedSchema()
1275 {
1276     /* Make sure that all properties of a same base feature type are */
1277     /* consecutive. If not, reorder */
1278     std::vector< std::vector<GMLPropertyDefn*> > aapoProps;
1279     GMLFeatureClass *poClass = poReader->GetClass(0);
1280     CPLString osJointClassName = "join";
1281 
1282     int iField, iSubClass;
1283     for( iField = 0; iField < poClass->GetPropertyCount(); iField ++ )
1284     {
1285         GMLPropertyDefn* poProp = poClass->GetProperty(iField);
1286         CPLString osPrefix(poProp->GetName());
1287         size_t iPos = osPrefix.find('.');
1288         if( iPos != std::string::npos )
1289             osPrefix.resize(iPos);
1290         for( iSubClass = 0; iSubClass < (int)aapoProps.size(); iSubClass ++ )
1291         {
1292             CPLString osPrefixClass(aapoProps[iSubClass][0]->GetName());
1293             size_t iPos = osPrefixClass.find('.');
1294             if( iPos != std::string::npos )
1295                 osPrefixClass.resize(iPos);
1296             if( osPrefix == osPrefixClass )
1297                 break;
1298         }
1299         if( iSubClass == (int)aapoProps.size() )
1300         {
1301             osJointClassName += "_";
1302             osJointClassName += osPrefix;
1303             aapoProps.push_back( std::vector<GMLPropertyDefn*>() );
1304         }
1305         aapoProps[iSubClass].push_back(poProp);
1306     }
1307     poClass->SetElementName(poClass->GetName());
1308     poClass->SetName(osJointClassName);
1309 
1310     poClass->StealProperties();
1311     std::vector< std::pair< CPLString, std::vector<GMLGeometryPropertyDefn*> > > aapoGeomProps;
1312     for( iSubClass = 0; iSubClass < (int)aapoProps.size(); iSubClass ++ )
1313     {
1314         CPLString osPrefixClass(aapoProps[iSubClass][0]->GetName());
1315         size_t iPos = osPrefixClass.find('.');
1316         if( iPos != std::string::npos )
1317             osPrefixClass.resize(iPos);
1318         aapoGeomProps.push_back( std::pair< CPLString, std::vector<GMLGeometryPropertyDefn*> >
1319                 (osPrefixClass, std::vector<GMLGeometryPropertyDefn*>()) );
1320         for( int iField = 0; iField < (int)aapoProps[iSubClass].size(); iField ++ )
1321         {
1322             poClass->AddProperty(aapoProps[iSubClass][iField]);
1323         }
1324     }
1325     aapoProps.resize(0);
1326 
1327     // Reorder geometry fields too
1328     for( iField = 0; iField < poClass->GetGeometryPropertyCount(); iField ++ )
1329     {
1330         GMLGeometryPropertyDefn* poProp = poClass->GetGeometryProperty(iField);
1331         CPLString osPrefix(poProp->GetName());
1332         size_t iPos = osPrefix.find('.');
1333         if( iPos != std::string::npos )
1334             osPrefix.resize(iPos);
1335         int iSubClass;
1336         for( iSubClass = 0; iSubClass < (int)aapoGeomProps.size(); iSubClass ++ )
1337         {
1338             if( osPrefix == aapoGeomProps[iSubClass].first )
1339                 break;
1340         }
1341         if( iSubClass == (int)aapoProps.size() )
1342             aapoGeomProps.push_back( std::pair< CPLString, std::vector<GMLGeometryPropertyDefn*> >
1343                     (osPrefix, std::vector<GMLGeometryPropertyDefn*>()) );
1344         aapoGeomProps[iSubClass].second.push_back(poProp);
1345     }
1346     poClass->StealGeometryProperties();
1347     for( iSubClass = 0; iSubClass < (int)aapoGeomProps.size(); iSubClass ++ )
1348     {
1349         for( iField = 0; iField < (int)aapoGeomProps[iSubClass].second.size(); iField ++ )
1350         {
1351             poClass->AddGeometryProperty(aapoGeomProps[iSubClass].second[iField]);
1352         }
1353     }
1354 }
1355 
1356 /************************************************************************/
1357 /*                         TranslateGMLSchema()                         */
1358 /************************************************************************/
1359 
TranslateGMLSchema(GMLFeatureClass * poClass)1360 OGRGMLLayer *OGRGMLDataSource::TranslateGMLSchema( GMLFeatureClass *poClass )
1361 
1362 {
1363     OGRGMLLayer *poLayer;
1364 
1365 /* -------------------------------------------------------------------- */
1366 /*      Create an empty layer.                                          */
1367 /* -------------------------------------------------------------------- */
1368 
1369     const char* pszSRSName = poClass->GetSRSName();
1370     OGRSpatialReference* poSRS = NULL;
1371     if (pszSRSName)
1372     {
1373         poSRS = new OGRSpatialReference();
1374         if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
1375         {
1376             delete poSRS;
1377             poSRS = NULL;
1378         }
1379     }
1380     else
1381     {
1382         pszSRSName = GetGlobalSRSName();
1383         if (pszSRSName)
1384         {
1385             poSRS = new OGRSpatialReference();
1386             if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
1387             {
1388                 delete poSRS;
1389                 poSRS = NULL;
1390             }
1391 
1392             if (poSRS != NULL && m_bInvertAxisOrderIfLatLong &&
1393                 GML_IsSRSLatLongOrder(pszSRSName))
1394             {
1395                 OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode( "GEOGCS" );
1396                 if( poGEOGCS != NULL )
1397                     poGEOGCS->StripNodes( "AXIS" );
1398 
1399                 OGR_SRSNode *poPROJCS = poSRS->GetAttrNode( "PROJCS" );
1400                 if (poPROJCS != NULL && poSRS->EPSGTreatsAsNorthingEasting())
1401                     poPROJCS->StripNodes( "AXIS" );
1402 
1403                 if (!poClass->HasExtents() &&
1404                     sBoundingRect.IsInit())
1405                 {
1406                     poClass->SetExtents(sBoundingRect.MinY,
1407                                         sBoundingRect.MaxY,
1408                                         sBoundingRect.MinX,
1409                                         sBoundingRect.MaxX);
1410                 }
1411             }
1412         }
1413 
1414         if (!poClass->HasExtents() &&
1415             sBoundingRect.IsInit())
1416         {
1417             poClass->SetExtents(sBoundingRect.MinX,
1418                                 sBoundingRect.MaxX,
1419                                 sBoundingRect.MinY,
1420                                 sBoundingRect.MaxY);
1421         }
1422     }
1423 
1424     /* Report a COMPD_CS only if GML_REPORT_COMPD_CS is explicitly set to TRUE */
1425     if( poSRS != NULL &&
1426         !CSLTestBoolean(CPLGetConfigOption("GML_REPORT_COMPD_CS", "FALSE")) )
1427     {
1428         OGR_SRSNode *poCOMPD_CS = poSRS->GetAttrNode( "COMPD_CS" );
1429         if( poCOMPD_CS != NULL )
1430         {
1431             OGR_SRSNode* poCandidateRoot = poCOMPD_CS->GetNode( "PROJCS" );
1432             if( poCandidateRoot == NULL )
1433                 poCandidateRoot = poCOMPD_CS->GetNode( "GEOGCS" );
1434             if( poCandidateRoot != NULL )
1435             {
1436                 poSRS->SetRoot(poCandidateRoot->Clone());
1437             }
1438         }
1439     }
1440 
1441 
1442     poLayer = new OGRGMLLayer( poClass->GetName(), FALSE, this );
1443 
1444 /* -------------------------------------------------------------------- */
1445 /*      Added attributes (properties).                                  */
1446 /* -------------------------------------------------------------------- */
1447     if (bExposeGMLId)
1448     {
1449         OGRFieldDefn oField( "gml_id", OFTString );
1450         oField.SetNullable(FALSE);
1451         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1452     }
1453     else if (bExposeFid)
1454     {
1455         OGRFieldDefn oField( "fid", OFTString );
1456         oField.SetNullable(FALSE);
1457         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1458     }
1459 
1460     int iField;
1461     for( iField = 0; iField < poClass->GetGeometryPropertyCount(); iField++ )
1462     {
1463         GMLGeometryPropertyDefn *poProperty = poClass->GetGeometryProperty( iField );
1464         OGRGeomFieldDefn oField( poProperty->GetName(), (OGRwkbGeometryType)poProperty->GetType() );
1465         if( poClass->GetGeometryPropertyCount() == 1 && poClass->GetFeatureCount() == 0 )
1466         {
1467             oField.SetType(wkbUnknown);
1468         }
1469         oField.SetSpatialRef(poSRS);
1470         oField.SetNullable(poProperty->IsNullable() );
1471         poLayer->GetLayerDefn()->AddGeomFieldDefn( &oField );
1472     }
1473 
1474     for( iField = 0; iField < poClass->GetPropertyCount(); iField++ )
1475     {
1476         GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
1477         OGRFieldType eFType;
1478 
1479         if( poProperty->GetType() == GMLPT_Untyped )
1480             eFType = OFTString;
1481         else if( poProperty->GetType() == GMLPT_String )
1482             eFType = OFTString;
1483         else if( poProperty->GetType() == GMLPT_Integer ||
1484                  poProperty->GetType() == GMLPT_Boolean ||
1485                  poProperty->GetType() == GMLPT_Short )
1486             eFType = OFTInteger;
1487         else if( poProperty->GetType() == GMLPT_Integer64 )
1488             eFType = OFTInteger64;
1489         else if( poProperty->GetType() == GMLPT_Real ||
1490                  poProperty->GetType() == GMLPT_Float )
1491             eFType = OFTReal;
1492         else if( poProperty->GetType() == GMLPT_StringList )
1493             eFType = OFTStringList;
1494         else if( poProperty->GetType() == GMLPT_IntegerList ||
1495                  poProperty->GetType() == GMLPT_BooleanList )
1496             eFType = OFTIntegerList;
1497         else if( poProperty->GetType() == GMLPT_Integer64List )
1498             eFType = OFTInteger64List;
1499         else if( poProperty->GetType() == GMLPT_RealList )
1500             eFType = OFTRealList;
1501         else if( poProperty->GetType() == GMLPT_FeaturePropertyList )
1502             eFType = OFTStringList;
1503         else
1504             eFType = OFTString;
1505 
1506         OGRFieldDefn oField( poProperty->GetName(), eFType );
1507         if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
1508           oField.SetName(poProperty->GetName()+4);
1509         if( poProperty->GetWidth() > 0 )
1510             oField.SetWidth( poProperty->GetWidth() );
1511         if( poProperty->GetPrecision() > 0 )
1512             oField.SetPrecision( poProperty->GetPrecision() );
1513         if( poProperty->GetType() == GMLPT_Boolean ||
1514             poProperty->GetType() == GMLPT_BooleanList )
1515             oField.SetSubType(OFSTBoolean);
1516         else if( poProperty->GetType() == GMLPT_Short)
1517             oField.SetSubType(OFSTInt16);
1518         else if( poProperty->GetType() == GMLPT_Float)
1519             oField.SetSubType(OFSTFloat32);
1520         if( !bEmptyAsNull )
1521             oField.SetNullable(poProperty->IsNullable() );
1522 
1523         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1524     }
1525 
1526     if( poSRS != NULL )
1527         poSRS->Release();
1528 
1529     return poLayer;
1530 }
1531 
1532 /************************************************************************/
1533 /*                         GetGlobalSRSName()                           */
1534 /************************************************************************/
1535 
GetGlobalSRSName()1536 const char *OGRGMLDataSource::GetGlobalSRSName()
1537 {
1538     if (poReader->CanUseGlobalSRSName() || bUseGlobalSRSName)
1539         return poReader->GetGlobalSRSName();
1540     else
1541         return NULL;
1542 }
1543 
1544 /************************************************************************/
1545 /*                               Create()                               */
1546 /************************************************************************/
1547 
Create(const char * pszFilename,char ** papszOptions)1548 int OGRGMLDataSource::Create( const char *pszFilename,
1549                               char **papszOptions )
1550 
1551 {
1552     if( fpOutput != NULL || poReader != NULL )
1553     {
1554         CPLAssert( FALSE );
1555         return FALSE;
1556     }
1557 
1558     if( strcmp(pszFilename,"/dev/stdout") == 0 )
1559         pszFilename = "/vsistdout/";
1560 
1561 /* -------------------------------------------------------------------- */
1562 /*      Read options                                                    */
1563 /* -------------------------------------------------------------------- */
1564 
1565     CSLDestroy(papszCreateOptions);
1566     papszCreateOptions = CSLDuplicate(papszOptions);
1567 
1568     const char* pszFormat = CSLFetchNameValue(papszCreateOptions, "FORMAT");
1569     bIsOutputGML3 = pszFormat && EQUAL(pszFormat, "GML3");
1570     bIsOutputGML3Deegree = pszFormat && EQUAL(pszFormat, "GML3Deegree");
1571     bIsOutputGML32 = pszFormat && EQUAL(pszFormat, "GML3.2");
1572     if (bIsOutputGML3Deegree || bIsOutputGML32)
1573         bIsOutputGML3 = TRUE;
1574 
1575     bIsLongSRSRequired =
1576         CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "GML3_LONGSRS", "YES"));
1577 
1578     bWriteSpaceIndentation =
1579         CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "SPACE_INDENTATION", "YES"));
1580 
1581 /* -------------------------------------------------------------------- */
1582 /*      Create the output file.                                         */
1583 /* -------------------------------------------------------------------- */
1584     pszName = CPLStrdup( pszFilename );
1585     osFilename = pszName;
1586 
1587     if( strcmp(pszFilename,"/vsistdout/") == 0 ||
1588         strncmp(pszFilename,"/vsigzip/", 9) == 0 )
1589     {
1590         fpOutput = VSIFOpenL(pszFilename, "wb");
1591         bFpOutputIsNonSeekable = TRUE;
1592         bFpOutputSingleFile = TRUE;
1593     }
1594     else if ( strncmp(pszFilename,"/vsizip/", 8) == 0)
1595     {
1596         if (EQUAL(CPLGetExtension(pszFilename), "zip"))
1597         {
1598             CPLFree(pszName);
1599             pszName = CPLStrdup(CPLFormFilename(pszFilename, "out.gml", NULL));
1600         }
1601 
1602         fpOutput = VSIFOpenL(pszName, "wb");
1603         bFpOutputIsNonSeekable = TRUE;
1604     }
1605     else
1606         fpOutput = VSIFOpenL( pszFilename, "wb+" );
1607     if( fpOutput == NULL )
1608     {
1609         CPLError( CE_Failure, CPLE_OpenFailed,
1610                   "Failed to create GML file %s.",
1611                   pszFilename );
1612         return FALSE;
1613     }
1614 
1615 /* -------------------------------------------------------------------- */
1616 /*      Write out "standard" header.                                    */
1617 /* -------------------------------------------------------------------- */
1618     PrintLine( fpOutput, "%s",
1619                 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" );
1620 
1621     if (!bFpOutputIsNonSeekable)
1622         nSchemaInsertLocation = (int) VSIFTellL( fpOutput );
1623 
1624     const char* pszPrefix = GetAppPrefix();
1625     const char* pszTargetNameSpace = CSLFetchNameValueDef(papszOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
1626 
1627     if( RemoveAppPrefix() )
1628         PrintLine( fpOutput, "<FeatureCollection" );
1629     else
1630         PrintLine( fpOutput, "<%s:FeatureCollection", pszPrefix );
1631 
1632     if (IsGML32Output())
1633     {
1634         char* pszGMLId = CPLEscapeString(
1635             CSLFetchNameValueDef(papszOptions, "GML_ID", "aFeatureCollection"), -1, CPLES_XML);
1636         PrintLine( fpOutput, "     gml:id=\"%s\"", pszGMLId );
1637         CPLFree(pszGMLId);
1638     }
1639 
1640 /* -------------------------------------------------------------------- */
1641 /*      Write out schema info if provided in creation options.          */
1642 /* -------------------------------------------------------------------- */
1643     const char *pszSchemaURI = CSLFetchNameValue(papszOptions,"XSISCHEMAURI");
1644     const char *pszSchemaOpt = CSLFetchNameValue( papszOptions, "XSISCHEMA" );
1645 
1646     if( pszSchemaURI != NULL )
1647     {
1648         PrintLine( fpOutput,
1649               "     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
1650         PrintLine( fpOutput,
1651               "     xsi:schemaLocation=\"%s\"",
1652                     pszSchemaURI );
1653     }
1654     else if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
1655     {
1656         char *pszBasename = CPLStrdup(CPLGetBasename( pszName ));
1657 
1658         PrintLine( fpOutput,
1659               "     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
1660         PrintLine( fpOutput,
1661               "     xsi:schemaLocation=\"%s %s\"",
1662                     pszTargetNameSpace,
1663                     CPLResetExtension( pszBasename, "xsd" ) );
1664         CPLFree( pszBasename );
1665     }
1666 
1667     if( RemoveAppPrefix() )
1668         PrintLine( fpOutput,
1669                 "     xmlns=\"%s\"", pszTargetNameSpace );
1670     else
1671         PrintLine( fpOutput,
1672                 "     xmlns:%s=\"%s\"", pszPrefix, pszTargetNameSpace );
1673 
1674     if (IsGML32Output())
1675         PrintLine( fpOutput, "%s",
1676                 "     xmlns:gml=\"http://www.opengis.net/gml/3.2\">" );
1677     else
1678         PrintLine( fpOutput, "%s",
1679                     "     xmlns:gml=\"http://www.opengis.net/gml\">" );
1680 
1681     return TRUE;
1682 }
1683 
1684 
1685 /************************************************************************/
1686 /*                         WriteTopElements()                           */
1687 /************************************************************************/
1688 
WriteTopElements()1689 void OGRGMLDataSource::WriteTopElements()
1690 {
1691     const char* pszDescription = CSLFetchNameValueDef(papszCreateOptions,
1692         "DESCRIPTION", GetMetadataItem("DESCRIPTION"));
1693     if( pszDescription != NULL )
1694     {
1695         if (bWriteSpaceIndentation)
1696             VSIFPrintfL( fpOutput, "  ");
1697         char* pszTmp = CPLEscapeString(pszDescription, -1, CPLES_XML);
1698         PrintLine( fpOutput, "<gml:description>%s</gml:description>", pszTmp );
1699         CPLFree(pszTmp);
1700     }
1701 
1702     const char* pszName = CSLFetchNameValueDef(papszCreateOptions,
1703         "NAME", GetMetadataItem("NAME"));
1704     if( pszName != NULL )
1705     {
1706         if (bWriteSpaceIndentation)
1707             VSIFPrintfL( fpOutput, "  ");
1708         char* pszTmp = CPLEscapeString(pszName, -1, CPLES_XML);
1709         PrintLine( fpOutput, "<gml:name>%s</gml:name>", pszTmp );
1710         CPLFree(pszTmp);
1711     }
1712 
1713 /* -------------------------------------------------------------------- */
1714 /*      Should we initialize an area to place the boundedBy element?    */
1715 /*      We will need to seek back to fill it in.                        */
1716 /* -------------------------------------------------------------------- */
1717     nBoundedByLocation = -1;
1718     if( CSLFetchBoolean( papszCreateOptions , "BOUNDEDBY", TRUE ))
1719     {
1720         if (!bFpOutputIsNonSeekable )
1721         {
1722             nBoundedByLocation = (int) VSIFTellL( fpOutput );
1723 
1724             if( nBoundedByLocation != -1 )
1725                 PrintLine( fpOutput, "%350s", "" );
1726         }
1727         else
1728         {
1729             if (bWriteSpaceIndentation)
1730                 VSIFPrintfL( fpOutput, "  ");
1731             if (IsGML3Output())
1732                 PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
1733             else
1734                 PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
1735         }
1736     }
1737 }
1738 
1739 /************************************************************************/
1740 /*                           ICreateLayer()                             */
1741 /************************************************************************/
1742 
1743 OGRLayer *
ICreateLayer(const char * pszLayerName,OGRSpatialReference * poSRS,OGRwkbGeometryType eType,CPL_UNUSED char ** papszOptions)1744 OGRGMLDataSource::ICreateLayer( const char * pszLayerName,
1745                                 OGRSpatialReference *poSRS,
1746                                 OGRwkbGeometryType eType,
1747                                 CPL_UNUSED char ** papszOptions )
1748 {
1749 /* -------------------------------------------------------------------- */
1750 /*      Verify we are in update mode.                                   */
1751 /* -------------------------------------------------------------------- */
1752     if( fpOutput == NULL )
1753     {
1754         CPLError( CE_Failure, CPLE_NoWriteAccess,
1755                   "Data source %s opened for read access.\n"
1756                   "New layer %s cannot be created.\n",
1757                   pszName, pszLayerName );
1758 
1759         return NULL;
1760     }
1761 
1762 /* -------------------------------------------------------------------- */
1763 /*      Ensure name is safe as an element name.                         */
1764 /* -------------------------------------------------------------------- */
1765     char *pszCleanLayerName = CPLStrdup( pszLayerName );
1766 
1767     CPLCleanXMLElementName( pszCleanLayerName );
1768     if( strcmp(pszCleanLayerName,pszLayerName) != 0 )
1769     {
1770         CPLError( CE_Warning, CPLE_AppDefined,
1771                   "Layer name '%s' adjusted to '%s' for XML validity.",
1772                   pszLayerName, pszCleanLayerName );
1773     }
1774 
1775 /* -------------------------------------------------------------------- */
1776 /*      Set or check validity of global SRS.                            */
1777 /* -------------------------------------------------------------------- */
1778     if (nLayers == 0)
1779     {
1780         WriteTopElements();
1781         if (poSRS)
1782             poWriteGlobalSRS = poSRS->Clone();
1783         bWriteGlobalSRS = TRUE;
1784     }
1785     else if( bWriteGlobalSRS )
1786     {
1787         if( poWriteGlobalSRS != NULL )
1788         {
1789             if (poSRS == NULL || !poSRS->IsSame(poWriteGlobalSRS))
1790             {
1791                 delete poWriteGlobalSRS;
1792                 poWriteGlobalSRS = NULL;
1793                 bWriteGlobalSRS = FALSE;
1794             }
1795         }
1796         else
1797         {
1798             if( poSRS != NULL )
1799                 bWriteGlobalSRS = FALSE;
1800         }
1801     }
1802 
1803 /* -------------------------------------------------------------------- */
1804 /*      Create the layer object.                                        */
1805 /* -------------------------------------------------------------------- */
1806     OGRGMLLayer *poLayer;
1807 
1808     poLayer = new OGRGMLLayer( pszCleanLayerName, TRUE, this );
1809     poLayer->GetLayerDefn()->SetGeomType(eType);
1810     if( eType != wkbNone )
1811     {
1812         poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetName("geometryProperty");
1813         if( poSRS != NULL )
1814         {
1815             /* Clone it since mapogroutput assumes that it can destroys */
1816             /* the SRS it has passed to use, instead of deferencing it */
1817             poSRS = poSRS->Clone();
1818             poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
1819             poSRS->Dereference();
1820         }
1821     }
1822 
1823     CPLFree( pszCleanLayerName );
1824 
1825 /* -------------------------------------------------------------------- */
1826 /*      Add layer to data source layer list.                            */
1827 /* -------------------------------------------------------------------- */
1828     papoLayers = (OGRGMLLayer **)
1829         CPLRealloc( papoLayers,  sizeof(OGRGMLLayer *) * (nLayers+1) );
1830 
1831     papoLayers[nLayers++] = poLayer;
1832 
1833     return poLayer;
1834 }
1835 
1836 /************************************************************************/
1837 /*                           TestCapability()                           */
1838 /************************************************************************/
1839 
TestCapability(const char * pszCap)1840 int OGRGMLDataSource::TestCapability( const char * pszCap )
1841 
1842 {
1843     if( EQUAL(pszCap,ODsCCreateLayer) )
1844         return TRUE;
1845     else if( EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) )
1846         return TRUE;
1847     else if( EQUAL(pszCap,ODsCCurveGeometries) )
1848         return bIsOutputGML3;
1849     else
1850         return FALSE;
1851 }
1852 
1853 /************************************************************************/
1854 /*                              GetLayer()                              */
1855 /************************************************************************/
1856 
GetLayer(int iLayer)1857 OGRLayer *OGRGMLDataSource::GetLayer( int iLayer )
1858 
1859 {
1860     if( iLayer < 0 || iLayer >= nLayers )
1861         return NULL;
1862     else
1863         return papoLayers[iLayer];
1864 }
1865 
1866 /************************************************************************/
1867 /*                            GrowExtents()                             */
1868 /************************************************************************/
1869 
GrowExtents(OGREnvelope3D * psGeomBounds,int nCoordDimension)1870 void OGRGMLDataSource::GrowExtents( OGREnvelope3D *psGeomBounds, int nCoordDimension )
1871 
1872 {
1873     sBoundingRect.Merge( *psGeomBounds );
1874     if (nCoordDimension == 3)
1875         bBBOX3D = TRUE;
1876 }
1877 
1878 /************************************************************************/
1879 /*                            InsertHeader()                            */
1880 /*                                                                      */
1881 /*      This method is used to update boundedby info for a              */
1882 /*      dataset, and insert schema descriptions depending on            */
1883 /*      selection options in effect.                                    */
1884 /************************************************************************/
1885 
InsertHeader()1886 void OGRGMLDataSource::InsertHeader()
1887 
1888 {
1889     VSILFILE        *fpSchema;
1890     int         nSchemaStart = 0;
1891 
1892     if( bFpOutputSingleFile )
1893         return;
1894 
1895 /* -------------------------------------------------------------------- */
1896 /*      Do we want to write the schema within the GML instance doc      */
1897 /*      or to a separate file?  For now we only support external.       */
1898 /* -------------------------------------------------------------------- */
1899     const char *pszSchemaURI = CSLFetchNameValue(papszCreateOptions,
1900                                                  "XSISCHEMAURI");
1901     const char *pszSchemaOpt = CSLFetchNameValue( papszCreateOptions,
1902                                                   "XSISCHEMA" );
1903 
1904     if( pszSchemaURI != NULL )
1905         return;
1906 
1907     if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
1908     {
1909         const char *pszXSDFilename = CPLResetExtension( pszName, "xsd" );
1910 
1911         fpSchema = VSIFOpenL( pszXSDFilename, "wt" );
1912         if( fpSchema == NULL )
1913         {
1914             CPLError( CE_Failure, CPLE_OpenFailed,
1915                       "Failed to open file %.500s for schema output.",
1916                       pszXSDFilename );
1917             return;
1918         }
1919         PrintLine( fpSchema, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );
1920     }
1921     else if( EQUAL(pszSchemaOpt,"INTERNAL") )
1922     {
1923         if (fpOutput == NULL)
1924             return;
1925         nSchemaStart = (int) VSIFTellL( fpOutput );
1926         fpSchema = fpOutput;
1927     }
1928     else
1929         return;
1930 
1931 /* ==================================================================== */
1932 /*      Write the schema section at the end of the file.  Once          */
1933 /*      complete, we will read it back in, and then move the whole      */
1934 /*      file "down" enough to insert the schema at the beginning.       */
1935 /* ==================================================================== */
1936 
1937 /* ==================================================================== */
1938 /*      Detect if there are fields of List types.                       */
1939 /* ==================================================================== */
1940     int iLayer;
1941     int bHasListFields = FALSE;
1942 
1943     for( iLayer = 0; !bHasListFields && iLayer < GetLayerCount(); iLayer++ )
1944     {
1945         OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn();
1946         for( int iField = 0; !bHasListFields && iField < poFDefn->GetFieldCount(); iField++ )
1947         {
1948             OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
1949 
1950             if( poFieldDefn->GetType() == OFTIntegerList ||
1951                 poFieldDefn->GetType() == OFTInteger64List ||
1952                 poFieldDefn->GetType() == OFTRealList ||
1953                 poFieldDefn->GetType() == OFTStringList )
1954             {
1955                 bHasListFields = TRUE;
1956             }
1957         } /* next field */
1958     } /* next layer */
1959 
1960 /* -------------------------------------------------------------------- */
1961 /*      Emit the start of the schema section.                           */
1962 /* -------------------------------------------------------------------- */
1963     const char* pszPrefix = GetAppPrefix();
1964     if( pszPrefix[0] == '\0' )
1965         pszPrefix = "ogr";
1966     const char* pszTargetNameSpace = CSLFetchNameValueDef(papszCreateOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
1967 
1968     if (IsGML3Output())
1969     {
1970         PrintLine( fpSchema,
1971                     "<xs:schema ");
1972         PrintLine( fpSchema,
1973                    "    targetNamespace=\"%s\"", pszTargetNameSpace );
1974         PrintLine( fpSchema,
1975                    "    xmlns:%s=\"%s\"",
1976                     pszPrefix, pszTargetNameSpace );
1977         PrintLine( fpSchema,
1978                    "    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"");
1979         if (IsGML32Output())
1980         {
1981             PrintLine( fpSchema,
1982                    "    xmlns:gml=\"http://www.opengis.net/gml/3.2\"");
1983             PrintLine( fpSchema,
1984                     "    xmlns:gmlsf=\"http://www.opengis.net/gmlsf/2.0\"");
1985         }
1986         else
1987         {
1988             PrintLine( fpSchema,
1989                     "    xmlns:gml=\"http://www.opengis.net/gml\"");
1990             if (!IsGML3DeegreeOutput())
1991             {
1992                 PrintLine( fpSchema,
1993                         "    xmlns:gmlsf=\"http://www.opengis.net/gmlsf\"");
1994             }
1995         }
1996         PrintLine( fpSchema,
1997                    "    elementFormDefault=\"qualified\"");
1998         PrintLine( fpSchema,
1999                    "    version=\"1.0\">");
2000 
2001         if (IsGML32Output())
2002         {
2003             PrintLine( fpSchema,
2004                     "<xs:annotation>");
2005             PrintLine( fpSchema,
2006                     "  <xs:appinfo source=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\">");
2007             PrintLine( fpSchema,
2008                     "    <gmlsf:ComplianceLevel>%d</gmlsf:ComplianceLevel>", (bHasListFields) ? 1 : 0);
2009             PrintLine( fpSchema,
2010                     "  </xs:appinfo>");
2011             PrintLine( fpSchema,
2012                     "</xs:annotation>");
2013 
2014             PrintLine( fpSchema,
2015                         "<xs:import namespace=\"http://www.opengis.net/gml/3.2\" schemaLocation=\"http://schemas.opengis.net/gml/3.2.1/gml.xsd\"/>" );
2016             PrintLine( fpSchema,
2017                         "<xs:import namespace=\"http://www.opengis.net/gmlsf/2.0\" schemaLocation=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\"/>" );
2018         }
2019         else
2020         {
2021             if (!IsGML3DeegreeOutput())
2022             {
2023                 PrintLine( fpSchema,
2024                         "<xs:annotation>");
2025                 PrintLine( fpSchema,
2026                         "  <xs:appinfo source=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\">");
2027                 PrintLine( fpSchema,
2028                         "    <gmlsf:ComplianceLevel>%d</gmlsf:ComplianceLevel>", (bHasListFields) ? 1 : 0);
2029                 PrintLine( fpSchema,
2030                         "    <gmlsf:GMLProfileSchema>http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd</gmlsf:GMLProfileSchema>");
2031                 PrintLine( fpSchema,
2032                         "  </xs:appinfo>");
2033                 PrintLine( fpSchema,
2034                         "</xs:annotation>");
2035             }
2036 
2037             PrintLine( fpSchema,
2038                         "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/>" );
2039             if (!IsGML3DeegreeOutput())
2040             {
2041                 PrintLine( fpSchema,
2042                             "<xs:import namespace=\"http://www.opengis.net/gmlsf\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\"/>" );
2043             }
2044         }
2045     }
2046     else
2047     {
2048         PrintLine( fpSchema,
2049                     "<xs:schema targetNamespace=\"%s\" xmlns:%s=\"%s\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:gml=\"http://www.opengis.net/gml\" elementFormDefault=\"qualified\" version=\"1.0\">",
2050                     pszTargetNameSpace, pszPrefix, pszTargetNameSpace );
2051 
2052         PrintLine( fpSchema,
2053                     "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/>" );
2054     }
2055 
2056 /* -------------------------------------------------------------------- */
2057 /*      Define the FeatureCollection                                    */
2058 /* -------------------------------------------------------------------- */
2059     if (IsGML3Output())
2060     {
2061         if (IsGML32Output())
2062         {
2063             PrintLine( fpSchema,
2064                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:AbstractGML\"/>",
2065                         pszPrefix );
2066         }
2067         else if (IsGML3DeegreeOutput())
2068         {
2069             PrintLine( fpSchema,
2070                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
2071                         pszPrefix );
2072         }
2073         else
2074         {
2075             PrintLine( fpSchema,
2076                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_GML\"/>",
2077                         pszPrefix );
2078         }
2079 
2080         PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
2081         PrintLine( fpSchema, "  <xs:complexContent>" );
2082         if (IsGML3DeegreeOutput())
2083         {
2084             PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
2085             PrintLine( fpSchema, "      <xs:sequence>" );
2086             PrintLine( fpSchema, "        <xs:element name=\"featureMember\" minOccurs=\"0\" maxOccurs=\"unbounded\">" );
2087         }
2088         else
2089         {
2090             PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureType\">" );
2091             PrintLine( fpSchema, "      <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" );
2092             PrintLine( fpSchema, "        <xs:element name=\"featureMember\">" );
2093         }
2094         PrintLine( fpSchema, "          <xs:complexType>" );
2095         if (IsGML32Output())
2096         {
2097             PrintLine( fpSchema, "            <xs:complexContent>" );
2098             PrintLine( fpSchema, "              <xs:extension base=\"gml:AbstractFeatureMemberType\">" );
2099             PrintLine( fpSchema, "                <xs:sequence>" );
2100             PrintLine( fpSchema, "                  <xs:element ref=\"gml:AbstractFeature\"/>" );
2101             PrintLine( fpSchema, "                </xs:sequence>" );
2102             PrintLine( fpSchema, "              </xs:extension>" );
2103             PrintLine( fpSchema, "            </xs:complexContent>" );
2104         }
2105         else
2106         {
2107             PrintLine( fpSchema, "            <xs:sequence>" );
2108             PrintLine( fpSchema, "              <xs:element ref=\"gml:_Feature\"/>" );
2109             PrintLine( fpSchema, "            </xs:sequence>" );
2110         }
2111         PrintLine( fpSchema, "          </xs:complexType>" );
2112         PrintLine( fpSchema, "        </xs:element>" );
2113         PrintLine( fpSchema, "      </xs:sequence>" );
2114         PrintLine( fpSchema, "    </xs:extension>" );
2115         PrintLine( fpSchema, "  </xs:complexContent>" );
2116         PrintLine( fpSchema, "</xs:complexType>" );
2117     }
2118     else
2119     {
2120         PrintLine( fpSchema,
2121                     "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
2122                     pszPrefix );
2123 
2124         PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
2125         PrintLine( fpSchema, "  <xs:complexContent>" );
2126         PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
2127         PrintLine( fpSchema, "      <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>" );
2128         PrintLine( fpSchema, "      <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>" );
2129         PrintLine( fpSchema, "    </xs:extension>" );
2130         PrintLine( fpSchema, "  </xs:complexContent>" );
2131         PrintLine( fpSchema, "</xs:complexType>" );
2132     }
2133 
2134 /* ==================================================================== */
2135 /*      Define the schema for each layer.                               */
2136 /* ==================================================================== */
2137 
2138     for( iLayer = 0; iLayer < GetLayerCount(); iLayer++ )
2139     {
2140         OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn();
2141 
2142 /* -------------------------------------------------------------------- */
2143 /*      Emit initial stuff for a feature type.                          */
2144 /* -------------------------------------------------------------------- */
2145         if (IsGML32Output())
2146         {
2147             PrintLine(
2148                 fpSchema,
2149                 "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:AbstractFeature\"/>",
2150                 poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
2151         }
2152         else
2153         {
2154             PrintLine(
2155                 fpSchema,
2156                 "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:_Feature\"/>",
2157                 poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
2158         }
2159 
2160         PrintLine( fpSchema, "<xs:complexType name=\"%s_Type\">", poFDefn->GetName());
2161         PrintLine( fpSchema, "  <xs:complexContent>");
2162         PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureType\">");
2163         PrintLine( fpSchema, "      <xs:sequence>");
2164 
2165         for( int iGeomField = 0; iGeomField < poFDefn->GetGeomFieldCount(); iGeomField++ )
2166         {
2167             OGRGeomFieldDefn *poFieldDefn = poFDefn->GetGeomFieldDefn(iGeomField);
2168 
2169     /* -------------------------------------------------------------------- */
2170     /*      Define the geometry attribute.                                  */
2171     /* -------------------------------------------------------------------- */
2172             const char* pszGeometryTypeName = "GeometryPropertyType";
2173             const char* pszComment = "";
2174             OGRwkbGeometryType eGType = wkbFlatten(poFieldDefn->GetType());
2175             switch(eGType)
2176             {
2177                 case wkbPoint:
2178                     pszGeometryTypeName = "PointPropertyType";
2179                     break;
2180                 case wkbLineString:
2181                 case wkbCircularString:
2182                 case wkbCompoundCurve:
2183                     if (IsGML3Output())
2184                     {
2185                         if( eGType == wkbLineString )
2186                             pszComment = " <!-- restricted to LineString -->";
2187                         else if( eGType == wkbCircularString )
2188                             pszComment = " <!-- contains CircularString -->";
2189                         else if( eGType == wkbCompoundCurve )
2190                             pszComment = " <!-- contains CompoundCurve -->";
2191                         pszGeometryTypeName = "CurvePropertyType";
2192                     }
2193                     else
2194                         pszGeometryTypeName = "LineStringPropertyType";
2195                     break;
2196                 case wkbPolygon:
2197                 case wkbCurvePolygon:
2198                     if (IsGML3Output())
2199                     {
2200                         if( eGType == wkbPolygon )
2201                             pszComment = " <!-- restricted to Polygon -->";
2202                         else if( eGType == wkbCurvePolygon )
2203                             pszComment = " <!-- contains CurvePolygon -->";
2204                         pszGeometryTypeName = "SurfacePropertyType";
2205                     }
2206                     else
2207                         pszGeometryTypeName = "PolygonPropertyType";
2208                     break;
2209                 case wkbMultiPoint:
2210                     pszGeometryTypeName = "MultiPointPropertyType";
2211                     break;
2212                 case wkbMultiLineString:
2213                 case wkbMultiCurve:
2214                     if (IsGML3Output())
2215                     {
2216                         if( eGType == wkbMultiLineString )
2217                             pszComment = " <!-- restricted to MultiLineString -->";
2218                         else if( eGType == wkbMultiCurve )
2219                             pszComment = " <!-- contains non-linear MultiCurve -->";
2220                         pszGeometryTypeName = "MultiCurvePropertyType";
2221                     }
2222                     else
2223                         pszGeometryTypeName = "MultiLineStringPropertyType";
2224                     break;
2225                 case wkbMultiPolygon:
2226                 case wkbMultiSurface:
2227                     if (IsGML3Output())
2228                     {
2229                         if( eGType == wkbMultiPolygon )
2230                             pszComment = " <!-- restricted to MultiPolygon -->";
2231                         else if( eGType == wkbMultiSurface )
2232                             pszComment = " <!-- contains non-linear MultiSurface -->";
2233                         pszGeometryTypeName = "MultiSurfacePropertyType";
2234                     }
2235                     else
2236                         pszGeometryTypeName = "MultiPolygonPropertyType";
2237                     break;
2238                 case wkbGeometryCollection:
2239                     pszGeometryTypeName = "MultiGeometryPropertyType";
2240                     break;
2241                 default:
2242                     break;
2243             }
2244 
2245             int nMinOccurs = poFieldDefn->IsNullable() ? 0 : 1;
2246             PrintLine( fpSchema,
2247                 "        <xs:element name=\"%s\" type=\"gml:%s\" nillable=\"true\" minOccurs=\"%d\" maxOccurs=\"1\"/>%s",
2248                        poFieldDefn->GetNameRef(), pszGeometryTypeName, nMinOccurs, pszComment );
2249         }
2250 
2251 /* -------------------------------------------------------------------- */
2252 /*      Emit each of the attributes.                                    */
2253 /* -------------------------------------------------------------------- */
2254         for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
2255         {
2256             OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
2257 
2258             if( IsGML3Output() && strcmp(poFieldDefn->GetNameRef(), "gml_id") == 0 )
2259                 continue;
2260             else if( !IsGML3Output() && strcmp(poFieldDefn->GetNameRef(), "fid") == 0 )
2261                 continue;
2262 
2263             int nMinOccurs = poFieldDefn->IsNullable() ? 0 : 1;
2264             if( poFieldDefn->GetType() == OFTInteger ||
2265                 poFieldDefn->GetType() == OFTIntegerList  )
2266             {
2267                 int nWidth;
2268 
2269                 if( poFieldDefn->GetWidth() > 0 )
2270                     nWidth = poFieldDefn->GetWidth();
2271                 else
2272                     nWidth = 16;
2273 
2274                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"%d\" maxOccurs=\"%s\">",
2275                            poFieldDefn->GetNameRef(),
2276                            nMinOccurs,
2277                            poFieldDefn->GetType() == OFTIntegerList ? "unbounded": "1" );
2278                 PrintLine( fpSchema, "          <xs:simpleType>");
2279                 if( poFieldDefn->GetSubType() == OFSTBoolean )
2280                 {
2281                     PrintLine( fpSchema, "            <xs:restriction base=\"xs:boolean\">");
2282                 }
2283                 else if( poFieldDefn->GetSubType() == OFSTInt16 )
2284                 {
2285                     PrintLine( fpSchema, "            <xs:restriction base=\"xs:short\">");
2286                 }
2287                 else
2288                 {
2289                     PrintLine( fpSchema, "            <xs:restriction base=\"xs:integer\">");
2290                     PrintLine( fpSchema, "              <xs:totalDigits value=\"%d\"/>", nWidth);
2291                 }
2292                 PrintLine( fpSchema, "            </xs:restriction>");
2293                 PrintLine( fpSchema, "          </xs:simpleType>");
2294                 PrintLine( fpSchema, "        </xs:element>");
2295             }
2296             else if( poFieldDefn->GetType() == OFTInteger64 ||
2297                      poFieldDefn->GetType() == OFTInteger64List  )
2298             {
2299                 int nWidth;
2300 
2301                 if( poFieldDefn->GetWidth() > 0 )
2302                     nWidth = poFieldDefn->GetWidth();
2303                 else
2304                     nWidth = 16;
2305 
2306                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"%d\" maxOccurs=\"%s\">",
2307                            poFieldDefn->GetNameRef(),
2308                            nMinOccurs,
2309                            poFieldDefn->GetType() == OFTInteger64List ? "unbounded": "1" );
2310                 PrintLine( fpSchema, "          <xs:simpleType>");
2311                 if( poFieldDefn->GetSubType() == OFSTBoolean )
2312                 {
2313                     PrintLine( fpSchema, "            <xs:restriction base=\"xs:boolean\">");
2314                 }
2315                 else if( poFieldDefn->GetSubType() == OFSTInt16 )
2316                 {
2317                     PrintLine( fpSchema, "            <xs:restriction base=\"xs:short\">");
2318                 }
2319                 else
2320                 {
2321                     PrintLine( fpSchema, "            <xs:restriction base=\"xs:long\">");
2322                     PrintLine( fpSchema, "              <xs:totalDigits value=\"%d\"/>", nWidth);
2323                 }
2324                 PrintLine( fpSchema, "            </xs:restriction>");
2325                 PrintLine( fpSchema, "          </xs:simpleType>");
2326                 PrintLine( fpSchema, "        </xs:element>");
2327             }
2328             else if( poFieldDefn->GetType() == OFTReal ||
2329                      poFieldDefn->GetType() == OFTRealList  )
2330             {
2331                 int nWidth, nDecimals;
2332 
2333                 nWidth = poFieldDefn->GetWidth();
2334                 nDecimals = poFieldDefn->GetPrecision();
2335 
2336                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"%d\" maxOccurs=\"%s\">",
2337                            poFieldDefn->GetNameRef(),
2338                            nMinOccurs,
2339                            poFieldDefn->GetType() == OFTRealList ? "unbounded": "1" );
2340                 PrintLine( fpSchema, "          <xs:simpleType>");
2341                 if( poFieldDefn->GetSubType() == OFSTFloat32 )
2342                     PrintLine( fpSchema, "            <xs:restriction base=\"xs:float\">");
2343                 else
2344                     PrintLine( fpSchema, "            <xs:restriction base=\"xs:decimal\">");
2345                 if (nWidth > 0)
2346                 {
2347                     PrintLine( fpSchema, "              <xs:totalDigits value=\"%d\"/>", nWidth);
2348                     PrintLine( fpSchema, "              <xs:fractionDigits value=\"%d\"/>", nDecimals);
2349                 }
2350                 PrintLine( fpSchema, "            </xs:restriction>");
2351                 PrintLine( fpSchema, "          </xs:simpleType>");
2352                 PrintLine( fpSchema, "        </xs:element>");
2353             }
2354             else if( poFieldDefn->GetType() == OFTString ||
2355                      poFieldDefn->GetType() == OFTStringList )
2356             {
2357                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"%d\" maxOccurs=\"%s\">",
2358                            poFieldDefn->GetNameRef(),
2359                            nMinOccurs,
2360                            poFieldDefn->GetType() == OFTStringList ? "unbounded": "1" );
2361                 PrintLine( fpSchema, "          <xs:simpleType>");
2362                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:string\">");
2363                 if( poFieldDefn->GetWidth() != 0 )
2364                 {
2365                     PrintLine( fpSchema, "              <xs:maxLength value=\"%d\"/>", poFieldDefn->GetWidth());
2366                 }
2367                 PrintLine( fpSchema, "            </xs:restriction>");
2368                 PrintLine( fpSchema, "          </xs:simpleType>");
2369                 PrintLine( fpSchema, "        </xs:element>");
2370             }
2371             else if( poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime )
2372             {
2373                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"%d\" maxOccurs=\"1\">",
2374                            poFieldDefn->GetNameRef(),
2375                            nMinOccurs );
2376                 PrintLine( fpSchema, "          <xs:simpleType>");
2377                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:string\">");
2378                 PrintLine( fpSchema, "            </xs:restriction>");
2379                 PrintLine( fpSchema, "          </xs:simpleType>");
2380                 PrintLine( fpSchema, "        </xs:element>");
2381             }
2382             else
2383             {
2384                 /* TODO */
2385             }
2386         } /* next field */
2387 
2388 /* -------------------------------------------------------------------- */
2389 /*      Finish off feature type.                                        */
2390 /* -------------------------------------------------------------------- */
2391         PrintLine( fpSchema, "      </xs:sequence>");
2392         PrintLine( fpSchema, "    </xs:extension>");
2393         PrintLine( fpSchema, "  </xs:complexContent>");
2394         PrintLine( fpSchema, "</xs:complexType>" );
2395     } /* next layer */
2396 
2397     PrintLine( fpSchema, "</xs:schema>" );
2398 
2399 /* ==================================================================== */
2400 /*      Move schema to the start of the file.                           */
2401 /* ==================================================================== */
2402     if( fpSchema == fpOutput )
2403     {
2404 /* -------------------------------------------------------------------- */
2405 /*      Read the schema into memory.                                    */
2406 /* -------------------------------------------------------------------- */
2407         int nSchemaSize = (int) VSIFTellL( fpOutput ) - nSchemaStart;
2408         char *pszSchema = (char *) CPLMalloc(nSchemaSize+1);
2409 
2410         VSIFSeekL( fpOutput, nSchemaStart, SEEK_SET );
2411 
2412         VSIFReadL( pszSchema, 1, nSchemaSize, fpOutput );
2413         pszSchema[nSchemaSize] = '\0';
2414 
2415 /* -------------------------------------------------------------------- */
2416 /*      Move file data down by "schema size" bytes from after <?xml>    */
2417 /*      header so we have room insert the schema.  Move in pretty       */
2418 /*      big chunks.                                                     */
2419 /* -------------------------------------------------------------------- */
2420         int nChunkSize = MIN(nSchemaStart-nSchemaInsertLocation,250000);
2421         char *pszChunk = (char *) CPLMalloc(nChunkSize);
2422         int nEndOfUnmovedData = nSchemaStart;
2423 
2424         for( nEndOfUnmovedData = nSchemaStart;
2425              nEndOfUnmovedData > nSchemaInsertLocation; )
2426         {
2427             int nBytesToMove =
2428                 MIN(nChunkSize, nEndOfUnmovedData - nSchemaInsertLocation );
2429 
2430             VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove, SEEK_SET );
2431             VSIFReadL( pszChunk, 1, nBytesToMove, fpOutput );
2432             VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove + nSchemaSize,
2433                       SEEK_SET );
2434             VSIFWriteL( pszChunk, 1, nBytesToMove, fpOutput );
2435 
2436             nEndOfUnmovedData -= nBytesToMove;
2437         }
2438 
2439         CPLFree( pszChunk );
2440 
2441 /* -------------------------------------------------------------------- */
2442 /*      Write the schema in the opened slot.                            */
2443 /* -------------------------------------------------------------------- */
2444         VSIFSeekL( fpOutput, nSchemaInsertLocation, SEEK_SET );
2445         VSIFWriteL( pszSchema, 1, nSchemaSize, fpOutput );
2446 
2447         VSIFSeekL( fpOutput, 0, SEEK_END );
2448 
2449         nBoundedByLocation += nSchemaSize;
2450 
2451         CPLFree(pszSchema);
2452     }
2453 /* -------------------------------------------------------------------- */
2454 /*      Close external schema files.                                    */
2455 /* -------------------------------------------------------------------- */
2456     else
2457         VSIFCloseL( fpSchema );
2458 }
2459 
2460 
2461 /************************************************************************/
2462 /*                            PrintLine()                               */
2463 /************************************************************************/
2464 
PrintLine(VSILFILE * fp,const char * fmt,...)2465 void OGRGMLDataSource::PrintLine(VSILFILE* fp, const char *fmt, ...)
2466 {
2467     CPLString osWork;
2468     va_list args;
2469 
2470     va_start( args, fmt );
2471     osWork.vPrintf( fmt, args );
2472     va_end( args );
2473 
2474 #ifdef WIN32
2475     const char* pszEOL = "\r\n";
2476 #else
2477     const char* pszEOL = "\n";
2478 #endif
2479 
2480     VSIFPrintfL(fp, "%s%s", osWork.c_str(), pszEOL);
2481 }
2482 
2483 
2484 /************************************************************************/
2485 /*                     OGRGMLSingleFeatureLayer                         */
2486 /************************************************************************/
2487 
2488 class OGRGMLSingleFeatureLayer : public OGRLayer
2489 {
2490   private:
2491     int                 nVal;
2492     OGRFeatureDefn     *poFeatureDefn;
2493     int                 iNextShapeId;
2494 
2495   public:
2496                         OGRGMLSingleFeatureLayer(int nVal );
~OGRGMLSingleFeatureLayer()2497                         ~OGRGMLSingleFeatureLayer() { poFeatureDefn->Release(); }
2498 
ResetReading()2499     virtual void        ResetReading() { iNextShapeId = 0; }
2500     virtual OGRFeature *GetNextFeature();
GetLayerDefn()2501     virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; }
TestCapability(const char *)2502     virtual int         TestCapability( const char * ) { return FALSE; }
2503 };
2504 
2505 /************************************************************************/
2506 /*                      OGRGMLSingleFeatureLayer()                      */
2507 /************************************************************************/
2508 
OGRGMLSingleFeatureLayer(int nVal)2509 OGRGMLSingleFeatureLayer::OGRGMLSingleFeatureLayer( int nVal )
2510 {
2511     poFeatureDefn = new OGRFeatureDefn( "SELECT" );
2512     poFeatureDefn->Reference();
2513     OGRFieldDefn oField( "Validates", OFTInteger );
2514     poFeatureDefn->AddFieldDefn( &oField );
2515 
2516     this->nVal = nVal;
2517     iNextShapeId = 0;
2518 }
2519 
2520 /************************************************************************/
2521 /*                           GetNextFeature()                           */
2522 /************************************************************************/
2523 
GetNextFeature()2524 OGRFeature * OGRGMLSingleFeatureLayer::GetNextFeature()
2525 {
2526     if (iNextShapeId != 0)
2527         return NULL;
2528 
2529     OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
2530     poFeature->SetField(0, nVal);
2531     poFeature->SetFID(iNextShapeId ++);
2532     return poFeature;
2533 }
2534 
2535 /************************************************************************/
2536 /*                            ExecuteSQL()                              */
2537 /************************************************************************/
2538 
ExecuteSQL(const char * pszSQLCommand,OGRGeometry * poSpatialFilter,const char * pszDialect)2539 OGRLayer * OGRGMLDataSource::ExecuteSQL( const char *pszSQLCommand,
2540                                          OGRGeometry *poSpatialFilter,
2541                                          const char *pszDialect )
2542 {
2543     if (poReader != NULL && EQUAL(pszSQLCommand, "SELECT ValidateSchema()"))
2544     {
2545         int bIsValid = FALSE;
2546         if (osXSDFilename.size())
2547         {
2548             CPLErrorReset();
2549             bIsValid = CPLValidateXML(osFilename, osXSDFilename, NULL);
2550         }
2551         return new OGRGMLSingleFeatureLayer(bIsValid);
2552     }
2553 
2554     return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
2555 }
2556 
2557 /************************************************************************/
2558 /*                          ReleaseResultSet()                          */
2559 /************************************************************************/
2560 
ReleaseResultSet(OGRLayer * poResultsSet)2561 void OGRGMLDataSource::ReleaseResultSet( OGRLayer * poResultsSet )
2562 {
2563     delete poResultsSet;
2564 }
2565 
2566 /************************************************************************/
2567 /*                          ExtractSRSName()                            */
2568 /************************************************************************/
2569 
ExtractSRSName(const char * pszXML,char * szSRSName,size_t sizeof_szSRSName)2570 static int ExtractSRSName(const char* pszXML, char* szSRSName,
2571                           size_t sizeof_szSRSName)
2572 {
2573     szSRSName[0] = '\0';
2574 
2575     const char* pszSRSName = strstr(pszXML, "srsName=\"");
2576     if( pszSRSName != NULL )
2577     {
2578         pszSRSName += 9;
2579         const char* pszEndQuote = strchr(pszSRSName, '"');
2580         if (pszEndQuote != NULL &&
2581             (size_t)(pszEndQuote - pszSRSName) < sizeof_szSRSName)
2582         {
2583             memcpy(szSRSName, pszSRSName, pszEndQuote - pszSRSName);
2584             szSRSName[pszEndQuote - pszSRSName] = '\0';
2585             return TRUE;
2586         }
2587     }
2588     return FALSE;
2589 }
2590 
2591 /************************************************************************/
2592 /*                      FindAndParseTopElements()                       */
2593 /************************************************************************/
2594 
FindAndParseTopElements(VSILFILE * fp)2595 void OGRGMLDataSource::FindAndParseTopElements(VSILFILE* fp)
2596 {
2597     /* Build a shortened XML file that contain only the global */
2598     /* boundedBy element, so as to be able to parse it easily */
2599 
2600     char szStartTag[128];
2601     char* pszXML = (char*)CPLMalloc(8192 + 128 + 3 + 1);
2602     VSIFSeekL(fp, 0, SEEK_SET);
2603     int nRead = (int)VSIFReadL(pszXML, 1, 8192, fp);
2604     pszXML[nRead] = 0;
2605 
2606     const char* pszStartTag = strchr(pszXML, '<');
2607     if (pszStartTag != NULL)
2608     {
2609         while (pszStartTag != NULL && pszStartTag[1] == '?')
2610             pszStartTag = strchr(pszStartTag + 1, '<');
2611 
2612         if (pszStartTag != NULL)
2613         {
2614             pszStartTag ++;
2615             const char* pszEndTag = strchr(pszStartTag, ' ');
2616             if (pszEndTag != NULL && pszEndTag - pszStartTag < 128 )
2617             {
2618                 memcpy(szStartTag, pszStartTag, pszEndTag - pszStartTag);
2619                 szStartTag[pszEndTag - pszStartTag] = '\0';
2620             }
2621             else
2622                 pszStartTag = NULL;
2623         }
2624     }
2625 
2626     const char* pszDescription = strstr(pszXML, "<gml:description>");
2627     if( pszDescription )
2628     {
2629         pszDescription += strlen("<gml:description>");
2630         const char* pszEndDescription = strstr(pszDescription,
2631                                                "</gml:description>");
2632         if( pszEndDescription )
2633         {
2634             CPLString osTmp(pszDescription);
2635             osTmp.resize(pszEndDescription-pszDescription);
2636             char* pszTmp = CPLUnescapeString(osTmp, NULL, CPLES_XML);
2637             if( pszTmp )
2638                 SetMetadataItem("DESCRIPTION", pszTmp);
2639             CPLFree(pszTmp);
2640         }
2641     }
2642 
2643     const char* pszName = strstr(pszXML, "<gml:name");
2644     if( pszName )
2645         pszName = strchr(pszName, '>');
2646     if( pszName )
2647     {
2648         pszName ++;
2649         const char* pszEndName = strstr(pszName, "</gml:name>");
2650         if( pszEndName )
2651         {
2652             CPLString osTmp(pszName);
2653             osTmp.resize(pszEndName-pszName);
2654             char* pszTmp = CPLUnescapeString(osTmp, NULL, CPLES_XML);
2655             if( pszTmp )
2656                 SetMetadataItem("NAME", pszTmp);
2657             CPLFree(pszTmp);
2658         }
2659     }
2660 
2661     char* pszEndBoundedBy = strstr(pszXML, "</wfs:boundedBy>");
2662     int bWFSBoundedBy = FALSE;
2663     if (pszEndBoundedBy != NULL)
2664         bWFSBoundedBy = TRUE;
2665     else
2666         pszEndBoundedBy = strstr(pszXML, "</gml:boundedBy>");
2667     if (pszStartTag != NULL && pszEndBoundedBy != NULL)
2668     {
2669         const char* pszSRSName = NULL;
2670         char szSRSName[128];
2671 
2672         szSRSName[0] = '\0';
2673 
2674         /* Find a srsName somewhere for some WFS 2.0 documents */
2675         /* that have not it set at the <wfs:boundedBy> element */
2676         /* e.g. http://geoserv.weichand.de:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAME=bvv:gmd_ex */
2677         if( bIsWFS )
2678         {
2679             ExtractSRSName(pszXML, szSRSName, sizeof(szSRSName));
2680         }
2681 
2682         pszEndBoundedBy[strlen("</gml:boundedBy>")] = '\0';
2683         strcat(pszXML, "</");
2684         strcat(pszXML, szStartTag);
2685         strcat(pszXML, ">");
2686 
2687         CPLPushErrorHandler(CPLQuietErrorHandler);
2688         CPLXMLNode* psXML = CPLParseXMLString(pszXML);
2689         CPLPopErrorHandler();
2690         CPLErrorReset();
2691         if (psXML != NULL)
2692         {
2693             CPLXMLNode* psBoundedBy = NULL;
2694             CPLXMLNode* psIter = psXML;
2695             while(psIter != NULL)
2696             {
2697                 psBoundedBy = CPLGetXMLNode(psIter, bWFSBoundedBy ?
2698                                         "wfs:boundedBy" : "gml:boundedBy");
2699                 if (psBoundedBy != NULL)
2700                     break;
2701                 psIter = psIter->psNext;
2702             }
2703 
2704             const char* pszLowerCorner = NULL;
2705             const char* pszUpperCorner = NULL;
2706             if (psBoundedBy != NULL)
2707             {
2708                 CPLXMLNode* psEnvelope = CPLGetXMLNode(psBoundedBy, "gml:Envelope");
2709                 if (psEnvelope)
2710                 {
2711                     pszSRSName = CPLGetXMLValue(psEnvelope, "srsName", NULL);
2712                     pszLowerCorner = CPLGetXMLValue(psEnvelope, "gml:lowerCorner", NULL);
2713                     pszUpperCorner = CPLGetXMLValue(psEnvelope, "gml:upperCorner", NULL);
2714                 }
2715             }
2716 
2717             if( bIsWFS && pszSRSName == NULL &&
2718                 pszLowerCorner != NULL && pszUpperCorner != NULL &&
2719                 szSRSName[0] != '\0' )
2720             {
2721                 pszSRSName = szSRSName;
2722             }
2723 
2724             if (pszSRSName != NULL && pszLowerCorner != NULL && pszUpperCorner != NULL)
2725             {
2726                 char** papszLC = CSLTokenizeString(pszLowerCorner);
2727                 char** papszUC = CSLTokenizeString(pszUpperCorner);
2728                 if (CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2)
2729                 {
2730                     CPLDebug("GML", "Global SRS = %s", pszSRSName);
2731 
2732                     if (strncmp(pszSRSName, "http://www.opengis.net/gml/srs/epsg.xml#", 40) == 0)
2733                     {
2734                         std::string osWork;
2735                         osWork.assign("EPSG:", 5);
2736                         osWork.append(pszSRSName+40);
2737                         poReader->SetGlobalSRSName(osWork.c_str());
2738                     }
2739                     else
2740                         poReader->SetGlobalSRSName(pszSRSName);
2741 
2742                     double dfMinX = CPLAtofM(papszLC[0]);
2743                     double dfMinY = CPLAtofM(papszLC[1]);
2744                     double dfMaxX = CPLAtofM(papszUC[0]);
2745                     double dfMaxY = CPLAtofM(papszUC[1]);
2746 
2747                     SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY);
2748                 }
2749                 CSLDestroy(papszLC);
2750                 CSLDestroy(papszUC);
2751             }
2752 
2753             CPLDestroyXMLNode(psXML);
2754         }
2755     }
2756 
2757     CPLFree(pszXML);
2758 }
2759 
2760 /************************************************************************/
2761 /*                             SetExtents()                             */
2762 /************************************************************************/
2763 
SetExtents(double dfMinX,double dfMinY,double dfMaxX,double dfMaxY)2764 void OGRGMLDataSource::SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
2765 {
2766     sBoundingRect.MinX = dfMinX;
2767     sBoundingRect.MinY = dfMinY;
2768     sBoundingRect.MaxX = dfMaxX;
2769     sBoundingRect.MaxY = dfMaxY;
2770 }
2771 
2772 /************************************************************************/
2773 /*                             GetAppPrefix()                           */
2774 /************************************************************************/
2775 
GetAppPrefix()2776 const char* OGRGMLDataSource::GetAppPrefix()
2777 {
2778     return CSLFetchNameValueDef(papszCreateOptions, "PREFIX", "ogr");
2779 }
2780 
2781 /************************************************************************/
2782 /*                            RemoveAppPrefix()                         */
2783 /************************************************************************/
2784 
RemoveAppPrefix()2785 int OGRGMLDataSource::RemoveAppPrefix()
2786 {
2787     if( CSLTestBoolean(CSLFetchNameValueDef(
2788             papszCreateOptions, "STRIP_PREFIX", "FALSE")) )
2789         return TRUE;
2790     const char* pszPrefix = GetAppPrefix();
2791     return( pszPrefix[0] == '\0' );
2792 }
2793 
2794 /************************************************************************/
2795 /*                        WriteFeatureBoundedBy()                       */
2796 /************************************************************************/
2797 
WriteFeatureBoundedBy()2798 int OGRGMLDataSource::WriteFeatureBoundedBy()
2799 {
2800     return CSLTestBoolean(CSLFetchNameValueDef(
2801                     papszCreateOptions, "WRITE_FEATURE_BOUNDED_BY", "TRUE"));
2802 }
2803 
2804 /************************************************************************/
2805 /*                          GetSRSDimensionLoc()                        */
2806 /************************************************************************/
2807 
GetSRSDimensionLoc()2808 const char* OGRGMLDataSource::GetSRSDimensionLoc()
2809 {
2810     return CSLFetchNameValue(papszCreateOptions, "SRSDIMENSION_LOC");
2811 }
2812