LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/gml - ogrgmldatasource.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 907 765 84.3 %
Date: 2013-03-30 Functions: 29 21 72.4 %

       1                 : /******************************************************************************
       2                 :  * $Id: ogrgmldatasource.cpp 25727 2013-03-10 14:56:33Z 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                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  *
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  *
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  *
      29                 :  ******************************************************************************
      30                 :  * Contributor: Alessandro Furieri, a.furieri@lqt.it
      31                 :  * Portions of this module implenting GML_SKIP_RESOLVE_ELEMS HUGE
      32                 :  * Developed for Faunalia ( http://www.faunalia.it) with funding from 
      33                 :  * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE
      34                 :  *
      35                 :  ****************************************************************************/
      36                 : 
      37                 : #include "ogr_gml.h"
      38                 : #include "parsexsd.h"
      39                 : #include "cpl_conv.h"
      40                 : #include "cpl_string.h"
      41                 : #include "cpl_http.h"
      42                 : #include "gmlutils.h"
      43                 : #include "ogr_p.h"
      44                 : 
      45                 : #include <vector>
      46                 : 
      47                 : CPL_CVSID("$Id: ogrgmldatasource.cpp 25727 2013-03-10 14:56:33Z rouault $");
      48                 : 
      49                 : /************************************************************************/
      50                 : /*                   ReplaceSpaceByPct20IfNeeded()                      */
      51                 : /************************************************************************/
      52                 : 
      53              50 : static CPLString ReplaceSpaceByPct20IfNeeded(const char* pszURL)
      54                 : {
      55                 :     /* Replace ' ' by '%20' */
      56              50 :     CPLString osRet = pszURL;
      57              50 :     const char* pszNeedle = strstr(pszURL, "text/xml; subtype=");
      58              50 :     if (pszNeedle)
      59                 :     {
      60               2 :         char* pszTmp = (char*)CPLMalloc(strlen(pszURL) + 2 +1);
      61               2 :         int nBeforeNeedle = (int)(pszNeedle - pszURL);
      62               2 :         memcpy(pszTmp, pszURL, nBeforeNeedle);
      63               2 :         strcpy(pszTmp + nBeforeNeedle, "text/xml;%20subtype=");
      64               2 :         strcpy(pszTmp + nBeforeNeedle + strlen("text/xml;%20subtype="), pszNeedle + strlen("text/xml; subtype="));
      65               2 :         osRet = pszTmp;
      66               2 :         CPLFree(pszTmp);
      67                 :     }
      68               0 :     return osRet;
      69                 : }
      70                 : 
      71                 : /************************************************************************/
      72                 : /*                         OGRGMLDataSource()                         */
      73                 : /************************************************************************/
      74                 : 
      75             507 : OGRGMLDataSource::OGRGMLDataSource()
      76                 : 
      77                 : {
      78             507 :     pszName = NULL;
      79             507 :     papoLayers = NULL;
      80             507 :     nLayers = 0;
      81                 : 
      82             507 :     poReader = NULL;
      83             507 :     fpOutput = NULL;
      84             507 :     bFpOutputIsNonSeekable = FALSE;
      85             507 :     bFpOutputSingleFile = FALSE;
      86             507 :     bIsOutputGML3 = FALSE;
      87             507 :     bIsOutputGML3Deegree = FALSE;
      88             507 :     bIsOutputGML32 = FALSE;
      89             507 :     bIsLongSRSRequired = FALSE;
      90             507 :     bWriteSpaceIndentation = TRUE;
      91                 : 
      92             507 :     papszCreateOptions = NULL;
      93             507 :     bOutIsTempFile = FALSE;
      94                 : 
      95             507 :     bExposeGMLId = FALSE;
      96             507 :     bExposeFid = FALSE;
      97             507 :     nSchemaInsertLocation = -1;
      98             507 :     nBoundedByLocation = -1;
      99             507 :     bBBOX3D = FALSE;
     100                 : 
     101             507 :     poGlobalSRS = NULL;
     102             507 :     bIsWFS = FALSE;
     103                 : 
     104             507 :     eReadMode = STANDARD;
     105             507 :     poStoredGMLFeature = NULL;
     106             507 :     poLastReadLayer = NULL;
     107                 : 
     108             507 :     m_bInvertAxisOrderIfLatLong = FALSE;
     109             507 :     m_bConsiderEPSGAsURN = FALSE;
     110             507 :     m_bGetSecondaryGeometryOption = FALSE;
     111             507 : }
     112                 : 
     113                 : /************************************************************************/
     114                 : /*                        ~OGRGMLDataSource()                         */
     115                 : /************************************************************************/
     116                 : 
     117             507 : OGRGMLDataSource::~OGRGMLDataSource()
     118                 : 
     119                 : {
     120                 : 
     121             507 :     if( fpOutput != NULL )
     122                 :     {
     123              18 :         const char* pszPrefix = GetAppPrefix();
     124                 : 
     125              18 :         PrintLine( fpOutput, "</%s:FeatureCollection>", pszPrefix );
     126                 : 
     127              18 :         if( bFpOutputIsNonSeekable)
     128                 :         {
     129               0 :             VSIFCloseL( fpOutput );
     130               0 :             fpOutput = NULL;
     131                 :         }
     132                 : 
     133              18 :         InsertHeader();
     134                 : 
     135              18 :         if( !bFpOutputIsNonSeekable
     136                 :             && nBoundedByLocation != -1
     137                 :             && VSIFSeekL( fpOutput, nBoundedByLocation, SEEK_SET ) == 0 )
     138                 :         {
     139              18 :             if (sBoundingRect.IsInit()  && IsGML3Output())
     140                 :             {
     141               4 :                 int bCoordSwap = FALSE;
     142                 :                 char* pszSRSName;
     143               4 :                 if (poGlobalSRS)
     144               4 :                     pszSRSName = GML_GetSRSName(poGlobalSRS, IsLongSRSRequired(), &bCoordSwap);
     145                 :                 else
     146               0 :                     pszSRSName = CPLStrdup("");
     147                 :                 char szLowerCorner[75], szUpperCorner[75];
     148               4 :                 if (bCoordSwap)
     149                 :                 {
     150               3 :                     OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinY, sBoundingRect.MinX, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
     151               3 :                     OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxY, sBoundingRect.MaxX, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2);
     152                 :                 }
     153                 :                 else
     154                 :                 {
     155               1 :                     OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinX, sBoundingRect.MinY, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
     156               1 :                     OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxX, sBoundingRect.MaxY, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2);
     157                 :                 }
     158               4 :                 if (bWriteSpaceIndentation)
     159               4 :                     VSIFPrintfL( fpOutput, "  ");
     160                 :                 PrintLine( fpOutput, "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner></gml:Envelope></gml:boundedBy>",
     161               4 :                            (bBBOX3D) ? " srsDimension=\"3\"" : "", pszSRSName, szLowerCorner, szUpperCorner);
     162               4 :                 CPLFree(pszSRSName);
     163                 :             }
     164              14 :             else if (sBoundingRect.IsInit())
     165                 :             {
     166               9 :                 if (bWriteSpaceIndentation)
     167               9 :                     VSIFPrintfL( fpOutput, "  ");
     168               9 :                 PrintLine( fpOutput, "<gml:boundedBy>" );
     169               9 :                 if (bWriteSpaceIndentation)
     170               9 :                     VSIFPrintfL( fpOutput, "    ");
     171               9 :                 PrintLine( fpOutput, "<gml:Box>" );
     172               9 :                 if (bWriteSpaceIndentation)
     173               9 :                     VSIFPrintfL( fpOutput, "      ");
     174                 :                 VSIFPrintfL( fpOutput,
     175                 :                             "<gml:coord><gml:X>%.16g</gml:X>"
     176                 :                             "<gml:Y>%.16g</gml:Y>",
     177               9 :                             sBoundingRect.MinX, sBoundingRect.MinY );
     178               9 :                 if (bBBOX3D)
     179                 :                     VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
     180               1 :                                sBoundingRect.MinZ );
     181               9 :                 PrintLine( fpOutput, "</gml:coord>");
     182               9 :                 if (bWriteSpaceIndentation)
     183               9 :                     VSIFPrintfL( fpOutput, "      ");
     184                 :                 VSIFPrintfL( fpOutput,
     185                 :                             "<gml:coord><gml:X>%.16g</gml:X>"
     186                 :                             "<gml:Y>%.16g</gml:Y>",
     187               9 :                             sBoundingRect.MaxX, sBoundingRect.MaxY );
     188               9 :                 if (bBBOX3D)
     189                 :                     VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
     190               1 :                                sBoundingRect.MaxZ );
     191               9 :                 PrintLine( fpOutput, "</gml:coord>");
     192               9 :                 if (bWriteSpaceIndentation)
     193               9 :                     VSIFPrintfL( fpOutput, "    ");
     194               9 :                 PrintLine( fpOutput, "</gml:Box>" );
     195               9 :                 if (bWriteSpaceIndentation)
     196               9 :                     VSIFPrintfL( fpOutput, "  ");
     197               9 :                 PrintLine( fpOutput, "</gml:boundedBy>" );
     198                 :             }
     199                 :             else
     200                 :             {
     201               5 :                 if (bWriteSpaceIndentation)
     202               5 :                     VSIFPrintfL( fpOutput, "  ");
     203               5 :                 if (IsGML3Output())
     204               0 :                     PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
     205                 :                 else
     206               5 :                     PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
     207                 :             }
     208                 :         }
     209                 : 
     210              18 :         if (fpOutput)
     211              18 :             VSIFCloseL( fpOutput );
     212                 :     }
     213                 : 
     214             507 :     CSLDestroy( papszCreateOptions );
     215             507 :     CPLFree( pszName );
     216                 : 
     217             665 :     for( int i = 0; i < nLayers; i++ )
     218             158 :         delete papoLayers[i];
     219                 :     
     220             507 :     CPLFree( papoLayers );
     221                 : 
     222             507 :     if( poReader )
     223                 :     {
     224             115 :         if (bOutIsTempFile)
     225               0 :             VSIUnlink(poReader->GetSourceFileName());
     226             115 :         delete poReader;
     227                 :     }
     228                 : 
     229             507 :     delete poGlobalSRS;
     230                 : 
     231             507 :     delete poStoredGMLFeature;
     232                 : 
     233             507 :     if (osXSDFilename.compare(CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this)) == 0)
     234               0 :         VSIUnlink(osXSDFilename);
     235             507 : }
     236                 : 
     237                 : /************************************************************************/
     238                 : /*                                Open()                                */
     239                 : /************************************************************************/
     240                 : 
     241             489 : int OGRGMLDataSource::Open( const char * pszNameIn, int bTestOpen )
     242                 : 
     243                 : {
     244                 :     VSILFILE   *fp;
     245                 :     char        szHeader[2048];
     246             489 :     int         nNumberOfFeatures = 0;
     247             489 :     CPLString   osWithVsiGzip;
     248             489 :     const char *pszSchemaLocation = NULL;
     249             489 :     int bCheckAuxFile = TRUE;
     250                 : 
     251                 : /* -------------------------------------------------------------------- */
     252                 : /*      Extract xsd filename from connexion string if present.          */
     253                 : /* -------------------------------------------------------------------- */
     254             489 :     osFilename = pszNameIn;
     255             489 :     const char *pszXSDFilenameTmp = strstr(pszNameIn, ",xsd=");
     256             489 :     if (pszXSDFilenameTmp != NULL)
     257                 :     {
     258              46 :         osFilename.resize(pszXSDFilenameTmp - pszNameIn);
     259              46 :         osXSDFilename = pszXSDFilenameTmp + strlen(",xsd=");
     260                 :     }
     261             489 :     const char *pszFilename = osFilename.c_str();
     262                 : 
     263             489 :     pszName = CPLStrdup( pszNameIn );
     264                 : 
     265                 : /* -------------------------------------------------------------------- */
     266                 : /*      Open the source file.                                           */
     267                 : /* -------------------------------------------------------------------- */
     268             489 :     fp = VSIFOpenL( pszFilename, "r" );
     269             489 :     if( fp == NULL )
     270                 :     {
     271             103 :         if( !bTestOpen )
     272                 :             CPLError( CE_Failure, CPLE_OpenFailed, 
     273                 :                       "Failed to open GML file `%s'.", 
     274               0 :                       pszFilename );
     275                 : 
     276             103 :         return FALSE;
     277                 :     }
     278                 : 
     279             386 :     int bExpatCompatibleEncoding = FALSE;
     280             386 :     int bHas3D = FALSE;
     281             386 :     int bHintConsiderEPSGAsURN = FALSE;
     282                 : 
     283                 : /* -------------------------------------------------------------------- */
     284                 : /*      If we aren't sure it is GML, load a header chunk and check      */
     285                 : /*      for signs it is GML                                             */
     286                 : /* -------------------------------------------------------------------- */
     287             386 :     if( bTestOpen )
     288                 :     {
     289             386 :         size_t nRead = VSIFReadL( szHeader, 1, sizeof(szHeader)-1, fp );
     290             386 :         if (nRead <= 0)
     291                 :         {
     292              15 :             VSIFCloseL( fp );
     293              15 :             return FALSE;
     294                 :         }
     295             371 :         szHeader[nRead] = '\0';
     296                 : 
     297                 :         /* Might be a OS-Mastermap gzipped GML, so let be nice and try to open */
     298                 :         /* it transparently with /vsigzip/ */
     299             371 :         if ( ((GByte*)szHeader)[0] == 0x1f && ((GByte*)szHeader)[1] == 0x8b &&
     300                 :              EQUAL(CPLGetExtension(pszFilename), "gz") &&
     301                 :              strncmp(pszFilename, "/vsigzip/", strlen("/vsigzip/")) != 0 )
     302                 :         {
     303               0 :             VSIFCloseL( fp );
     304               0 :             osWithVsiGzip = "/vsigzip/";
     305               0 :             osWithVsiGzip += pszFilename;
     306                 : 
     307               0 :             pszFilename = osWithVsiGzip;
     308                 : 
     309               0 :             fp = VSIFOpenL( pszFilename, "r" );
     310               0 :             if( fp == NULL )
     311               0 :                 return FALSE;
     312                 : 
     313               0 :             nRead = VSIFReadL( szHeader, 1, sizeof(szHeader) - 1, fp );
     314               0 :             if (nRead <= 0)
     315                 :             {
     316               0 :                 VSIFCloseL( fp );
     317               0 :                 return FALSE;
     318                 :             }
     319               0 :             szHeader[nRead] = '\0';
     320                 :         }
     321                 : 
     322                 : /* -------------------------------------------------------------------- */
     323                 : /*      Check for a UTF-8 BOM and skip if found                         */
     324                 : /*                                                                      */
     325                 : /*      TODO: BOM is variable-lenght parameter and depends on encoding. */
     326                 : /*            Add BOM detection for other encodings.                    */
     327                 : /* -------------------------------------------------------------------- */
     328                 : 
     329                 :         // Used to skip to actual beginning of XML data
     330             371 :         char* szPtr = szHeader;
     331                 : 
     332             373 :         if( ( (unsigned char)szHeader[0] == 0xEF )
     333               1 :             && ( (unsigned char)szHeader[1] == 0xBB )
     334               1 :             && ( (unsigned char)szHeader[2] == 0xBF) )
     335                 :         {
     336               1 :             szPtr += 3;
     337                 :         }
     338                 : 
     339             371 :         const char* pszEncoding = strstr(szPtr, "encoding=");
     340             371 :         if (pszEncoding)
     341             260 :             bExpatCompatibleEncoding = (pszEncoding[9] == '\'' || pszEncoding[9] == '"') &&
     342                 :                                        (EQUALN(pszEncoding + 10, "UTF-8", 5) ||
     343                 :                                         EQUALN(pszEncoding + 10, "ISO-8859-15", 11) ||
     344                 :                                         (EQUALN(pszEncoding + 10, "ISO-8859-1", 10) &&
     345             392 :                                          pszEncoding[20] == pszEncoding[9])) ;
     346                 :         else
     347             239 :             bExpatCompatibleEncoding = TRUE; /* utf-8 is the default */
     348                 : 
     349             371 :         bHas3D = strstr(szPtr, "srsDimension=\"3\"") != NULL || strstr(szPtr, "<gml:Z>") != NULL;
     350                 : 
     351                 : /* -------------------------------------------------------------------- */
     352                 : /*      Here, we expect the opening chevrons of GML tree root element   */
     353                 : /* -------------------------------------------------------------------- */
     354             371 :         if( szPtr[0] != '<' 
     355                 :             || strstr(szPtr,"opengis.net/gml") == NULL )
     356                 :         {
     357             252 :             VSIFCloseL( fp );
     358             252 :             return FALSE;
     359                 :         }
     360                 : 
     361                 :         /* Ignore GeoRSS documents. They will be recognized by the GeoRSS driver */
     362             119 :         if( strstr(szPtr, "<rss") != NULL && strstr(szPtr, "xmlns:georss") != NULL )
     363                 :         {
     364               4 :             VSIFCloseL( fp );
     365               4 :             return FALSE;
     366                 :         }
     367                 : 
     368                 :         /* Small optimization: if we parse a <wfs:FeatureCollection>  and */
     369                 :         /* that numberOfFeatures is set, we can use it to set the FeatureCount */
     370                 :         /* but *ONLY* if there's just one class ! */
     371             115 :         const char* pszFeatureCollection = strstr(szPtr, "wfs:FeatureCollection");
     372             115 :         if (pszFeatureCollection == NULL)
     373              62 :             pszFeatureCollection = strstr(szPtr, "gml:FeatureCollection"); /* GML 3.2.1 output */
     374             115 :         if (pszFeatureCollection == NULL)
     375                 :         {
     376              52 :             pszFeatureCollection = strstr(szPtr, "<FeatureCollection"); /* Deegree WFS 1.0.0 output */
     377              52 :             if (pszFeatureCollection && strstr(szPtr, "xmlns:wfs=\"http://www.opengis.net/wfs\"") == NULL)
     378               1 :                 pszFeatureCollection = NULL;
     379                 :         }
     380             115 :         if (pszFeatureCollection)
     381                 :         {
     382              64 :             bExposeGMLId = TRUE;
     383              64 :             bIsWFS = TRUE;
     384              64 :             const char* pszNumberOfFeatures = strstr(szPtr, "numberOfFeatures=");
     385              64 :             if (pszNumberOfFeatures)
     386                 :             {
     387              39 :                 pszNumberOfFeatures += 17;
     388              39 :                 char ch = pszNumberOfFeatures[0];
     389              39 :                 if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
     390                 :                 {
     391              39 :                     nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
     392                 :                 }
     393                 :             }
     394              25 :             else if ((pszNumberOfFeatures = strstr(szPtr, "numberReturned=")) != NULL) /* WFS 2.0.0 */
     395                 :             {
     396               3 :                 pszNumberOfFeatures += 15;
     397               3 :                 char ch = pszNumberOfFeatures[0];
     398               3 :                 if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
     399                 :                 {
     400                 :                     /* 'unknown' might be a valid value in a corrected version of WFS 2.0 */
     401                 :                     /* but it will also evaluate to 0, that is considered as unknown, so nothing */
     402                 :                     /* particular to do */
     403               3 :                     nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
     404                 :                 }
     405                 :             }
     406                 :         }
     407              51 :         else if (strncmp(pszFilename, "/vsimem/tempwfs_", strlen("/vsimem/tempwfs_")) == 0)
     408                 :         {
     409                 :             /* http://regis.intergraph.com/wfs/dcmetro/request.asp? returns a <G:FeatureCollection> */
     410                 :             /* Who knows what servers can return ? Ok, so when in the context of the WFS driver */
     411                 :             /* always expose the gml:id to avoid later crashes */
     412               0 :             bExposeGMLId = TRUE;
     413               0 :             bIsWFS = TRUE;
     414                 :         }
     415                 :         else
     416                 :         {
     417                 :             bExposeGMLId = strstr(szPtr, " gml:id=\"") != NULL ||
     418              51 :                            strstr(szPtr, " gml:id='") != NULL;
     419                 :             bExposeFid = strstr(szPtr, " fid=\"") != NULL ||
     420              51 :                          strstr(szPtr, " fid='") != NULL;
     421                 :             
     422              51 :             const char* pszExposeGMLId = CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL);
     423              51 :             if (pszExposeGMLId)
     424               0 :                 bExposeGMLId = CSLTestBoolean(pszExposeGMLId);
     425                 : 
     426              51 :             const char* pszExposeFid = CPLGetConfigOption("GML_EXPOSE_FID", NULL);
     427              51 :             if (pszExposeFid)
     428               1 :                 bExposeFid = CSLTestBoolean(pszExposeFid);
     429                 :         }
     430                 : 
     431             115 :         bHintConsiderEPSGAsURN = strstr(szPtr, "xmlns:fme=\"http://www.safe.com/gml/fme\"") != NULL;
     432                 : 
     433             115 :         pszSchemaLocation = strstr(szPtr, "schemaLocation=");
     434             115 :         if (pszSchemaLocation)
     435              91 :             pszSchemaLocation += strlen("schemaLocation=");
     436                 : 
     437             115 :         if (bIsWFS && EQUALN(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")))
     438              43 :             bCheckAuxFile = FALSE;
     439                 :     }
     440                 : 
     441                 : /* -------------------------------------------------------------------- */
     442                 : /*      We assume now that it is GML.  Instantiate a GMLReader on it.   */
     443                 : /* -------------------------------------------------------------------- */
     444                 : 
     445             115 :     const char* pszReadMode = CPLGetConfigOption("GML_READ_MODE", NULL);
     446             227 :     if (pszReadMode == NULL || EQUAL(pszReadMode, "STANDARD"))
     447             112 :         eReadMode = STANDARD;
     448               3 :     else if (EQUAL(pszReadMode, "SEQUENTIAL_LAYERS"))
     449               2 :         eReadMode = SEQUENTIAL_LAYERS;
     450               1 :     else if (EQUAL(pszReadMode, "INTERLEAVED_LAYERS"))
     451               1 :         eReadMode = INTERLEAVED_LAYERS;
     452                 :     else
     453                 :     {
     454               0 :         CPLDebug("GML", "Unrecognized value for GML_READ_MODE configuration option.");
     455                 :     }
     456                 : 
     457                 :     m_bInvertAxisOrderIfLatLong = CSLTestBoolean(
     458             115 :         CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES"));
     459                 : 
     460                 :     const char* pszConsiderEPSGAsURN =
     461             115 :         CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", NULL);
     462             115 :     if (pszConsiderEPSGAsURN != NULL)
     463               0 :         m_bConsiderEPSGAsURN = CSLTestBoolean(pszConsiderEPSGAsURN);
     464             115 :     else if (bHintConsiderEPSGAsURN)
     465                 :     {
     466                 :         /* GML produced by FME (at least CanVec GML) seem to honour EPSG axis ordering */
     467               5 :         CPLDebug("GML", "FME-produced GML --> consider that GML_CONSIDER_EPSG_AS_URN is set to YES");
     468               5 :         m_bConsiderEPSGAsURN = TRUE;
     469                 :     }
     470                 :     else
     471             110 :         m_bConsiderEPSGAsURN = FALSE;
     472                 : 
     473             115 :     m_bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO"));
     474                 : 
     475                 :     /* EXPAT is faster than Xerces, so when it is safe to use it, use it ! */
     476                 :     /* The only interest of Xerces is for rare encodings that Expat doesn't handle */
     477                 :     /* but UTF-8 is well handled by Expat */
     478             115 :     int bUseExpatParserPreferably = bExpatCompatibleEncoding;
     479                 : 
     480                 :     /* Override default choice */
     481             115 :     const char* pszGMLParser = CPLGetConfigOption("GML_PARSER", NULL);
     482             115 :     if (pszGMLParser)
     483                 :     {
     484               0 :         if (EQUAL(pszGMLParser, "EXPAT"))
     485               0 :             bUseExpatParserPreferably = TRUE;
     486               0 :         else if (EQUAL(pszGMLParser, "XERCES"))
     487               0 :             bUseExpatParserPreferably = FALSE;
     488                 :     }
     489                 : 
     490                 :     poReader = CreateGMLReader( bUseExpatParserPreferably,
     491                 :                                 m_bInvertAxisOrderIfLatLong,
     492                 :                                 m_bConsiderEPSGAsURN,
     493             115 :                                 m_bGetSecondaryGeometryOption );
     494             115 :     if( poReader == NULL )
     495                 :     {
     496                 :         CPLError( CE_Failure, CPLE_AppDefined, 
     497                 :                   "File %s appears to be GML but the GML reader can't\n"
     498                 :                   "be instantiated, likely because Xerces or Expat support wasn't\n"
     499                 :                   "configured in.", 
     500               0 :                   pszFilename );
     501               0 :         VSIFCloseL( fp );
     502               0 :         return FALSE;
     503                 :     }
     504                 : 
     505             115 :     poReader->SetSourceFile( pszFilename );
     506                 : 
     507                 : /* -------------------------------------------------------------------- */
     508                 : /*      Find <gml:boundedBy>                                            */
     509                 : /* -------------------------------------------------------------------- */
     510                 : 
     511             115 :     FindAndParseBoundedBy(fp);
     512                 : 
     513                 : /* -------------------------------------------------------------------- */
     514                 : /*      Resolve the xlinks in the source file and save it with the      */
     515                 : /*      extension ".resolved.gml". The source file will to set to that. */
     516                 : /* -------------------------------------------------------------------- */
     517                 : 
     518             115 :     char *pszXlinkResolvedFilename = NULL;
     519             115 :     const char *pszOption = CPLGetConfigOption("GML_SAVE_RESOLVED_TO", NULL);
     520             115 :     int bResolve = TRUE;
     521             115 :     int bHugeFile = FALSE;
     522             115 :     if( pszOption != NULL && EQUALN( pszOption, "SAME", 4 ) )
     523                 :     {
     524                 :         // "SAME" will overwrite the existing gml file
     525               0 :         pszXlinkResolvedFilename = CPLStrdup( pszFilename );
     526                 :     }
     527             115 :     else if( pszOption != NULL &&
     528                 :              CPLStrnlen( pszOption, 5 ) >= 5 &&
     529                 :              EQUALN( pszOption - 4 + strlen( pszOption ), ".gml", 4 ) )
     530                 :     {
     531                 :         // Any string ending with ".gml" will try and write to it
     532               2 :         pszXlinkResolvedFilename = CPLStrdup( pszOption );
     533                 :     }
     534                 :     else
     535                 :     {
     536                 :         // When no option is given or is not recognised,
     537                 :         // use the same file name with the extension changed to .resolved.gml
     538                 :         pszXlinkResolvedFilename = CPLStrdup(
     539             113 :                             CPLResetExtension( pszFilename, "resolved.gml" ) );
     540                 : 
     541                 :         // Check if the file already exists.
     542                 :         VSIStatBufL sResStatBuf, sGMLStatBuf;
     543             113 :         if( bCheckAuxFile && VSIStatL( pszXlinkResolvedFilename, &sResStatBuf ) == 0 )
     544                 :         {
     545               3 :             VSIStatL( pszFilename, &sGMLStatBuf );
     546               3 :             if( sGMLStatBuf.st_mtime > sResStatBuf.st_mtime )
     547                 :             {
     548                 :                 CPLDebug( "GML", 
     549                 :                           "Found %s but ignoring because it appears\n"
     550                 :                           "be older than the associated GML file.", 
     551               0 :                           pszXlinkResolvedFilename );
     552                 :             }
     553                 :             else
     554                 :             {
     555               3 :                 poReader->SetSourceFile( pszXlinkResolvedFilename );
     556               3 :                 bResolve = FALSE;
     557                 :             }
     558                 :         }
     559                 :     }
     560                 : 
     561                 :     const char *pszSkipOption = CPLGetConfigOption( "GML_SKIP_RESOLVE_ELEMS",
     562             115 :                                                     "ALL");
     563             115 :     char **papszSkip = NULL;
     564             115 :     if( EQUAL( pszSkipOption, "ALL" ) )
     565             108 :         bResolve = FALSE;
     566               7 :     else if( EQUAL( pszSkipOption, "HUGE" ) )//exactly as NONE, but intended for HUGE files
     567               2 :         bHugeFile = TRUE;
     568               5 :     else if( !EQUAL( pszSkipOption, "NONE" ) )//use this to resolve everything
     569                 :         papszSkip = CSLTokenizeString2( pszSkipOption, ",",
     570                 :                                            CSLT_STRIPLEADSPACES |
     571               1 :                                            CSLT_STRIPENDSPACES );
     572             115 :     int         bHaveSchema = FALSE;
     573             115 :     int         bSchemaDone = FALSE;
     574                 :  
     575                 : /* -------------------------------------------------------------------- */
     576                 : /*      Is some GML Feature Schema (.gfs) TEMPLATE required ?           */
     577                 : /* -------------------------------------------------------------------- */
     578                 :     const char *pszGFSTemplateName = 
     579             115 :                 CPLGetConfigOption( "GML_GFS_TEMPLATE", NULL);
     580             115 :     if( pszGFSTemplateName != NULL )
     581                 :     {
     582                 :         /* attempting to load the GFS TEMPLATE */
     583               0 :         bHaveSchema = poReader->LoadClasses( pszGFSTemplateName );
     584                 :     } 
     585                 : 
     586             115 :     if( bResolve )
     587                 :     {
     588               7 :         if ( bHugeFile )
     589                 :         {
     590               2 :             bSchemaDone = TRUE;
     591                 :             int bSqliteIsTempFile =
     592               2 :                 CSLTestBoolean(CPLGetConfigOption( "GML_HUGE_TEMPFILE", "YES"));
     593               2 :             int iSqliteCacheMB = atoi(CPLGetConfigOption( "OGR_SQLITE_CACHE", "0"));
     594               4 :             if( poReader->HugeFileResolver( pszXlinkResolvedFilename,
     595                 :                                             bSqliteIsTempFile, 
     596               2 :                                             iSqliteCacheMB ) == FALSE )
     597                 :             {
     598                 :                 // we assume an errors have been reported.
     599               0 :                 VSIFCloseL(fp);
     600               0 :                 return FALSE;
     601                 :             }
     602                 :         }
     603                 :         else
     604                 :         {
     605                 :             poReader->ResolveXlinks( pszXlinkResolvedFilename,
     606                 :                                      &bOutIsTempFile,
     607               5 :                                      papszSkip );
     608                 :         }
     609                 :     }
     610                 : 
     611             115 :     CPLFree( pszXlinkResolvedFilename );
     612             115 :     pszXlinkResolvedFilename = NULL;
     613             115 :     CSLDestroy( papszSkip );
     614             115 :     papszSkip = NULL;
     615                 : 
     616                 :     /* If the source filename for the reader is still the GML filename, then */
     617                 :     /* we can directly provide the file pointer. Otherwise we close it */
     618             115 :     if (strcmp(poReader->GetSourceFileName(), pszFilename) == 0)
     619             105 :         poReader->SetFP(fp);
     620                 :     else
     621              10 :         VSIFCloseL(fp);
     622             115 :     fp = NULL;
     623                 : 
     624                 :     /* Is a prescan required ? */
     625             115 :     if( bHaveSchema && !bSchemaDone )
     626                 :     {
     627                 :         /* We must detect which layers are actually present in the .gml */
     628                 :         /* and how many features they have */
     629               0 :         if( !poReader->PrescanForTemplate() )
     630                 :         {
     631                 :             // we assume an errors have been reported.
     632               0 :             return FALSE;
     633                 :         }
     634                 :     }
     635                 : 
     636             115 :     CPLString osGFSFilename = CPLResetExtension( pszFilename, "gfs" );
     637             115 :     if (strncmp(osGFSFilename, "/vsigzip/", strlen("/vsigzip/")) == 0)
     638               0 :         osGFSFilename = osGFSFilename.substr(strlen("/vsigzip/"));
     639                 : 
     640                 : /* -------------------------------------------------------------------- */
     641                 : /*      Can we find a GML Feature Schema (.gfs) for the input file?     */
     642                 : /* -------------------------------------------------------------------- */
     643             115 :     if( !bHaveSchema && osXSDFilename.size() == 0)
     644                 :     {
     645                 :         VSIStatBufL sGFSStatBuf;
     646              71 :         if( bCheckAuxFile && VSIStatL( osGFSFilename, &sGFSStatBuf ) == 0 )
     647                 :         {
     648                 :             VSIStatBufL sGMLStatBuf;
     649              19 :             VSIStatL( pszFilename, &sGMLStatBuf );
     650              19 :             if( sGMLStatBuf.st_mtime > sGFSStatBuf.st_mtime )
     651                 :             {
     652                 :                 CPLDebug( "GML", 
     653                 :                           "Found %s but ignoring because it appears\n"
     654                 :                           "be older than the associated GML file.", 
     655               0 :                           osGFSFilename.c_str() );
     656                 :             }
     657                 :             else
     658                 :             {
     659              19 :                 bHaveSchema = poReader->LoadClasses( osGFSFilename );
     660              19 :                 if (bHaveSchema)
     661                 :                 {
     662                 :                     const char *pszXSDFilenameTmp;
     663              19 :                     pszXSDFilenameTmp = CPLResetExtension( pszFilename, "xsd" );
     664              19 :                     if( VSIStatExL( pszXSDFilenameTmp, &sGMLStatBuf,
     665                 :                                     VSI_STAT_EXISTS_FLAG ) == 0 )
     666                 :                     {
     667                 :                         CPLDebug("GML", "Using %s file, ignoring %s",
     668               0 :                                  osGFSFilename.c_str(), pszXSDFilenameTmp);
     669                 :                     }
     670                 :                 }
     671                 :             }
     672                 :         }
     673                 :     }
     674                 : 
     675                 : /* -------------------------------------------------------------------- */
     676                 : /*      Can we find an xsd which might conform to tbe GML3 Level 0      */
     677                 : /*      profile?  We really ought to look for it based on the rules     */
     678                 : /*      schemaLocation in the GML feature collection but for now we     */
     679                 : /*      just hopes it is in the same director with the same name.       */
     680                 : /* -------------------------------------------------------------------- */
     681             115 :     int bHasFoundXSD = FALSE;
     682                 : 
     683             115 :     if( !bHaveSchema )
     684                 :     {
     685              96 :         char** papszTypeNames = NULL;
     686                 : 
     687                 :         VSIStatBufL sXSDStatBuf;
     688              96 :         if (osXSDFilename.size() == 0)
     689                 :         {
     690              52 :             osXSDFilename = CPLResetExtension( pszFilename, "xsd" );
     691              52 :             if( bCheckAuxFile && VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
     692                 :             {
     693              24 :                 bHasFoundXSD = TRUE;
     694                 :             }
     695                 :         }
     696                 :         else
     697                 :         {
     698              44 :             if ( VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
     699                 :             {
     700              44 :                 bHasFoundXSD = TRUE;
     701                 :             }
     702                 :         }
     703                 : 
     704                 :         /* For WFS, try to fetch the application schema */
     705             246 :         if( bIsWFS && pszSchemaLocation != NULL && 
     706             100 :             (pszSchemaLocation[0] == '\'' || pszSchemaLocation[0] == '"') &&
     707              50 :              strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) != NULL )
     708                 :         {
     709              50 :             char* pszSchemaLocationTmp1 = CPLStrdup(pszSchemaLocation + 1);
     710              50 :             int nTruncLen = (int)(strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) - (pszSchemaLocation + 1));
     711              50 :             pszSchemaLocationTmp1[nTruncLen] = '\0';
     712                 :             char* pszSchemaLocationTmp2 = CPLUnescapeString(
     713              50 :                 pszSchemaLocationTmp1, NULL, CPLES_XML);
     714              50 :             CPLString osEscaped = ReplaceSpaceByPct20IfNeeded(pszSchemaLocationTmp2);
     715              50 :             CPLFree(pszSchemaLocationTmp2);
     716              50 :             pszSchemaLocationTmp2 = CPLStrdup(osEscaped);
     717              50 :             if (pszSchemaLocationTmp2)
     718                 :             {
     719                 :                 /* pszSchemaLocationTmp2 is of the form : */
     720                 :                 /* http://namespace1 http://namespace1_schema_location http://namespace2 http://namespace1_schema_location2 */
     721                 :                 /* So we try to find http://namespace1_schema_location that contains hints that it is the WFS application */
     722                 :                 /* schema, i.e. if it contains typename= and request=DescribeFeatureType */
     723              50 :                 char** papszTokens = CSLTokenizeString2(pszSchemaLocationTmp2, " \r\n", 0);
     724              50 :                 int nTokens = CSLCount(papszTokens);
     725              50 :                 if ((nTokens % 2) == 0)
     726                 :                 {
     727             147 :                     for(int i=0;i<nTokens;i+=2)
     728                 :                     {
     729              97 :                         char* pszLocation = CPLUnescapeString(papszTokens[i+1], NULL, CPLES_URL);
     730              97 :                         CPLString osLocation = pszLocation;
     731              97 :                         CPLFree(pszLocation);
     732              97 :                         if (osLocation.ifind("typename=") != std::string::npos &&
     733                 :                             osLocation.ifind("request=DescribeFeatureType") != std::string::npos)
     734                 :                         {
     735              43 :                             CPLString osTypeName = CPLURLGetValue(osLocation, "typename");
     736              43 :                             papszTypeNames = CSLTokenizeString2( osTypeName, ",", 0);
     737                 : 
     738              43 :                             if (!bHasFoundXSD && CPLHTTPEnabled() &&
     739                 :                                 CSLTestBoolean(CPLGetConfigOption("GML_DOWNLOAD_WFS_SCHEMA", "YES")))
     740                 :                             {
     741               0 :                                 CPLString osEscaped = ReplaceSpaceByPct20IfNeeded(osLocation);
     742               0 :                                 CPLHTTPResult* psResult = CPLHTTPFetch(osEscaped, NULL);
     743               0 :                                 if (psResult)
     744                 :                                 {
     745               0 :                                     if (psResult->nStatus == 0 && psResult->pabyData != NULL)
     746                 :                                     {
     747               0 :                                         bHasFoundXSD = TRUE;
     748                 :                                         osXSDFilename =
     749               0 :                                             CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this);
     750                 :                                         VSILFILE* fpMem = VSIFileFromMemBuffer(
     751                 :                                             osXSDFilename, psResult->pabyData,
     752               0 :                                             psResult->nDataLen, TRUE);
     753               0 :                                         VSIFCloseL(fpMem);
     754               0 :                                         psResult->pabyData = NULL;
     755                 :                                     }
     756               0 :                                     CPLHTTPDestroyResult(psResult);
     757               0 :                                 }
     758              43 :                             }
     759                 :                         }
     760                 :                     }
     761                 :                 }
     762              50 :                 CSLDestroy(papszTokens);
     763                 :             }
     764              50 :             CPLFree(pszSchemaLocationTmp2);
     765              50 :             CPLFree(pszSchemaLocationTmp1);
     766                 :         }
     767                 : 
     768              96 :         if( bHasFoundXSD )
     769                 :         {
     770              68 :             std::vector<GMLFeatureClass*> aosClasses;
     771              68 :             bHaveSchema = GMLParseXSD( osXSDFilename, aosClasses );
     772              68 :             if (bHaveSchema)
     773                 :             {
     774              68 :                 CPLDebug("GML", "Using %s", osXSDFilename.c_str());
     775                 : 
     776              68 :                 std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
     777              68 :                 std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
     778             210 :                 while (iter != eiter)
     779                 :                 {
     780              74 :                     GMLFeatureClass* poClass = *iter;
     781              74 :                     iter ++;
     782                 : 
     783                 :                     /* We have no way of knowing if the geometry type is 25D */
     784                 :                     /* when examining the xsd only, so if there was a hint */
     785                 :                     /* it is, we force to 25D */
     786              74 :                     if (bHas3D)
     787                 :                     {
     788                 :                         poClass->SetGeometryType(
     789               2 :                             poClass->GetGeometryType() | wkb25DBit);
     790                 :                     }
     791                 : 
     792              74 :                     int bAddClass = TRUE;
     793                 :                     /* If typenames are declared, only register the matching classes, in case */
     794                 :                     /* the XSD contains more layers */
     795              74 :                     if (papszTypeNames != NULL)
     796                 :                     {
     797              43 :                         bAddClass = FALSE;
     798              43 :                         char** papszIter = papszTypeNames;
     799             129 :                         while (*papszIter && !bAddClass)
     800                 :                         {
     801              43 :                             const char* pszTypeName = *papszIter;
     802              43 :                             if (strcmp(pszTypeName, poClass->GetName()) == 0)
     803               2 :                                 bAddClass = TRUE;
     804              43 :                             papszIter ++;
     805                 :                         }
     806                 : 
     807                 :                         /* Retry by removing prefixes */
     808              43 :                         if (!bAddClass)
     809                 :                         {
     810              41 :                             papszIter = papszTypeNames;
     811             123 :                             while (*papszIter && !bAddClass)
     812                 :                             {
     813              41 :                                 const char* pszTypeName = *papszIter;
     814              41 :                                 const char* pszColon = strchr(pszTypeName, ':');
     815              41 :                                 if (pszColon)
     816                 :                                 {
     817              41 :                                     pszTypeName = pszColon + 1;
     818              41 :                                     if (strcmp(pszTypeName, poClass->GetName()) == 0)
     819                 :                                     {
     820              41 :                                         poClass->SetName(pszTypeName);
     821              41 :                                         bAddClass = TRUE;
     822                 :                                     }
     823                 :                                 }
     824              41 :                                 papszIter ++;
     825                 :                             }
     826                 :                         }
     827                 : 
     828                 :                     }
     829                 : 
     830              74 :                     if (bAddClass)
     831              74 :                         poReader->AddClass( poClass );
     832                 :                     else
     833               0 :                         delete poClass;
     834                 :                 }
     835              68 :                 poReader->SetClassListLocked( TRUE );
     836              68 :             }
     837                 :         }
     838                 : 
     839              96 :         if (bHaveSchema && bIsWFS)
     840                 :         {
     841                 :             /* For WFS, we can assume sequential layers */
     842              52 :             if (poReader->GetClassCount() > 1 && pszReadMode == NULL)
     843                 :             {
     844               1 :                 CPLDebug("GML", "WFS output. Using SEQUENTIAL_LAYERS read mode");
     845               1 :                 eReadMode = SEQUENTIAL_LAYERS;
     846                 :             }
     847                 :             /* Sometimes the returned schema contains only <xs:include> that we don't resolve */
     848                 :             /* so ignore it */
     849              51 :             else if (poReader->GetClassCount() == 0)
     850               0 :                 bHaveSchema = FALSE;
     851                 :         }
     852                 : 
     853              96 :         CSLDestroy(papszTypeNames);
     854                 :     }
     855                 : 
     856                 : /* -------------------------------------------------------------------- */
     857                 : /*      Force a first pass to establish the schema.  Eventually we      */
     858                 : /*      will have mechanisms for remembering the schema and related     */
     859                 : /*      information.                                                    */
     860                 : /* -------------------------------------------------------------------- */
     861             115 :     if( !bHaveSchema )
     862                 :     {
     863              28 :         if( !poReader->PrescanForSchema( TRUE ) )
     864                 :         {
     865                 :             // we assume an errors have been reported.
     866               3 :             return FALSE;
     867                 :         }
     868                 : 
     869              25 :         if( bHasFoundXSD )
     870                 :         {
     871                 :             CPLDebug("GML", "Generating %s file, ignoring %s",
     872               0 :                      osGFSFilename.c_str(), osXSDFilename.c_str());
     873                 :         }
     874                 :     }
     875                 : 
     876             112 :     if (poReader->GetClassCount() > 1 && poReader->IsSequentialLayers() &&
     877                 :         pszReadMode == NULL)
     878                 :     {
     879              11 :         CPLDebug("GML", "Layers are monoblock. Using SEQUENTIAL_LAYERS read mode");
     880              11 :         eReadMode = SEQUENTIAL_LAYERS;
     881                 :     }
     882                 : 
     883                 : /* -------------------------------------------------------------------- */
     884                 : /*      Save the schema file if possible.  Don't make a fuss if we      */
     885                 : /*      can't ... could be read-only directory or something.            */
     886                 : /* -------------------------------------------------------------------- */
     887             112 :     if( !bHaveSchema && !poReader->HasStoppedParsing() &&
     888                 :         !EQUALN(pszFilename, "/vsitar/", strlen("/vsitar/")) &&
     889                 :         !EQUALN(pszFilename, "/vsizip/", strlen("/vsizip/")) &&
     890                 :         !EQUALN(pszFilename, "/vsigzip/vsi", strlen("/vsigzip/vsi")) &&
     891                 :         !EQUALN(pszFilename, "/vsigzip//vsi", strlen("/vsigzip//vsi")) &&
     892                 :         !EQUALN(pszFilename, "/vsicurl/", strlen("/vsicurl/")) &&
     893                 :         !EQUALN(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")))
     894                 :     {
     895              25 :         VSILFILE    *fp = NULL;
     896                 : 
     897                 :         VSIStatBufL sGFSStatBuf;
     898              25 :         if( VSIStatExL( osGFSFilename, &sGFSStatBuf, VSI_STAT_EXISTS_FLAG ) != 0
     899                 :             && (fp = VSIFOpenL( osGFSFilename, "wt" )) != NULL )
     900                 :         {
     901              25 :             VSIFCloseL( fp );
     902              25 :             poReader->SaveClasses( osGFSFilename );
     903                 :         }
     904                 :         else
     905                 :         {
     906                 :             CPLDebug("GML", 
     907                 :                      "Not saving %s files already exists or can't be created.",
     908               0 :                      osGFSFilename.c_str() );
     909                 :         }
     910                 :     }
     911                 : 
     912                 : /* -------------------------------------------------------------------- */
     913                 : /*      Translate the GMLFeatureClasses into layers.                    */
     914                 : /* -------------------------------------------------------------------- */
     915                 :     papoLayers = (OGRGMLLayer **)
     916             112 :         CPLCalloc( sizeof(OGRGMLLayer *), poReader->GetClassCount());
     917             112 :     nLayers = 0;
     918                 : 
     919             112 :     if (poReader->GetClassCount() == 1 && nNumberOfFeatures != 0)
     920                 :     {
     921              39 :         GMLFeatureClass *poClass = poReader->GetClass(0);
     922              39 :         int nFeatureCount = poClass->GetFeatureCount();
     923              39 :         if (nFeatureCount < 0)
     924                 :         {
     925              37 :             poClass->SetFeatureCount(nNumberOfFeatures);
     926                 :         }
     927               2 :         else if (nFeatureCount != nNumberOfFeatures)
     928                 :         {
     929               0 :             CPLDebug("GML", "Feature count in header, and actual feature count don't match");
     930                 :         }
     931                 :     }
     932                 : 
     933             364 :     while( nLayers < poReader->GetClassCount() )
     934                 :     {
     935             140 :         papoLayers[nLayers] = TranslateGMLSchema(poReader->GetClass(nLayers));
     936             140 :         nLayers++;
     937                 :     }
     938                 :     
     939                 : 
     940                 :     
     941             112 :     return TRUE;
     942                 : }
     943                 : 
     944                 : /************************************************************************/
     945                 : /*                         TranslateGMLSchema()                         */
     946                 : /************************************************************************/
     947                 : 
     948             140 : OGRGMLLayer *OGRGMLDataSource::TranslateGMLSchema( GMLFeatureClass *poClass )
     949                 : 
     950                 : {
     951                 :     OGRGMLLayer *poLayer;
     952                 :     OGRwkbGeometryType eGType 
     953             140 :         = (OGRwkbGeometryType) poClass->GetGeometryType();
     954                 : 
     955             140 :     if( poClass->GetFeatureCount() == 0 )
     956               0 :         eGType = wkbUnknown;
     957                 :     
     958                 : /* -------------------------------------------------------------------- */
     959                 : /*      Create an empty layer.                                          */
     960                 : /* -------------------------------------------------------------------- */
     961                 : 
     962             140 :     const char* pszSRSName = poClass->GetSRSName();
     963             140 :     OGRSpatialReference* poSRS = NULL;
     964             140 :     if (pszSRSName)
     965                 :     {
     966              19 :         poSRS = new OGRSpatialReference();
     967              19 :         if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
     968                 :         {
     969               0 :             delete poSRS;
     970               0 :             poSRS = NULL;
     971                 :         }
     972                 :     }
     973             121 :     else if (bIsWFS && poReader->GetClassCount() == 1)
     974                 :     {
     975              55 :         pszSRSName = GetGlobalSRSName();
     976              55 :         if (pszSRSName)
     977                 :         {
     978              38 :             poSRS = new OGRSpatialReference();
     979              38 :             if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
     980                 :             {
     981               0 :                 delete poSRS;
     982               0 :                 poSRS = NULL;
     983                 :             }
     984                 : 
     985              38 :             if (poSRS != NULL && m_bInvertAxisOrderIfLatLong &&
     986                 :                 GML_IsSRSLatLongOrder(pszSRSName))
     987                 :             {
     988               4 :                 OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode( "GEOGCS" );
     989               4 :                 if( poGEOGCS != NULL )
     990               4 :                     poGEOGCS->StripNodes( "AXIS" );
     991                 : 
     992               4 :                 OGR_SRSNode *poPROJCS = poSRS->GetAttrNode( "PROJCS" );
     993               4 :                 if (poPROJCS != NULL && poSRS->EPSGTreatsAsNorthingEasting())
     994               0 :                     poPROJCS->StripNodes( "AXIS" );
     995                 : 
     996               4 :                 if (!poClass->HasExtents() &&
     997                 :                     sBoundingRect.IsInit())
     998                 :                 {
     999                 :                     poClass->SetExtents(sBoundingRect.MinY,
    1000                 :                                         sBoundingRect.MaxY,
    1001                 :                                         sBoundingRect.MinX,
    1002               4 :                                         sBoundingRect.MaxX);
    1003                 :                 }
    1004                 :             }
    1005                 :         }
    1006                 : 
    1007              55 :         if (!poClass->HasExtents() &&
    1008                 :             sBoundingRect.IsInit())
    1009                 :         {
    1010                 :             poClass->SetExtents(sBoundingRect.MinX,
    1011                 :                                 sBoundingRect.MaxX,
    1012                 :                                 sBoundingRect.MinY,
    1013              34 :                                 sBoundingRect.MaxY);
    1014                 :         }
    1015                 :     }
    1016                 : 
    1017                 :     poLayer = new OGRGMLLayer( poClass->GetName(), poSRS, FALSE,
    1018             140 :                                eGType, this );
    1019             197 :     delete poSRS;
    1020                 : 
    1021                 : /* -------------------------------------------------------------------- */
    1022                 : /*      Added attributes (properties).                                  */
    1023                 : /* -------------------------------------------------------------------- */
    1024             140 :     if (bExposeGMLId)
    1025                 :     {
    1026             106 :         OGRFieldDefn oField( "gml_id", OFTString );
    1027             106 :         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
    1028                 :     }
    1029              34 :     else if (bExposeFid)
    1030                 :     {
    1031              22 :         OGRFieldDefn oField( "fid", OFTString );
    1032              22 :         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
    1033                 :     }
    1034                 : 
    1035             543 :     for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
    1036                 :     {
    1037             403 :         GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
    1038                 :         OGRFieldType eFType;
    1039                 : 
    1040             403 :         if( poProperty->GetType() == GMLPT_Untyped )
    1041               0 :             eFType = OFTString;
    1042             403 :         else if( poProperty->GetType() == GMLPT_String )
    1043             157 :             eFType = OFTString;
    1044             246 :         else if( poProperty->GetType() == GMLPT_Integer )
    1045             183 :             eFType = OFTInteger;
    1046              63 :         else if( poProperty->GetType() == GMLPT_Real )
    1047              51 :             eFType = OFTReal;
    1048              12 :         else if( poProperty->GetType() == GMLPT_StringList )
    1049               6 :             eFType = OFTStringList;
    1050               6 :         else if( poProperty->GetType() == GMLPT_IntegerList )
    1051               3 :             eFType = OFTIntegerList;
    1052               3 :         else if( poProperty->GetType() == GMLPT_RealList )
    1053               3 :             eFType = OFTRealList;
    1054                 :         else
    1055               0 :             eFType = OFTString;
    1056                 :         
    1057             403 :         OGRFieldDefn oField( poProperty->GetName(), eFType );
    1058             403 :         if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
    1059               0 :           oField.SetName(poProperty->GetName()+4);
    1060             403 :         if( poProperty->GetWidth() > 0 )
    1061              92 :             oField.SetWidth( poProperty->GetWidth() );
    1062             403 :         if( poProperty->GetPrecision() > 0 )
    1063               8 :             oField.SetPrecision( poProperty->GetPrecision() );
    1064                 : 
    1065             403 :         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
    1066                 :     }
    1067                 : 
    1068             140 :     return poLayer;
    1069                 : }
    1070                 : 
    1071                 : /************************************************************************/
    1072                 : /*                         GetGlobalSRSName()                           */
    1073                 : /************************************************************************/
    1074                 : 
    1075             653 : const char *OGRGMLDataSource::GetGlobalSRSName()
    1076                 : {
    1077             653 :     if (poReader->CanUseGlobalSRSName() || bIsWFS)
    1078             552 :         return poReader->GetGlobalSRSName();
    1079                 :     else
    1080             101 :         return NULL;
    1081                 : }
    1082                 : 
    1083                 : /************************************************************************/
    1084                 : /*                               Create()                               */
    1085                 : /************************************************************************/
    1086                 : 
    1087              18 : int OGRGMLDataSource::Create( const char *pszFilename, 
    1088                 :                               char **papszOptions )
    1089                 : 
    1090                 : {
    1091              18 :     if( fpOutput != NULL || poReader != NULL )
    1092                 :     {
    1093               0 :         CPLAssert( FALSE );
    1094               0 :         return FALSE;
    1095                 :     }
    1096                 : 
    1097              18 :     if( strcmp(pszFilename,"/dev/stdout") == 0 )
    1098               0 :         pszFilename = "/vsistdout/";
    1099                 : 
    1100                 : /* -------------------------------------------------------------------- */
    1101                 : /*      Read options                                                    */
    1102                 : /* -------------------------------------------------------------------- */
    1103                 : 
    1104              18 :     CSLDestroy(papszCreateOptions);
    1105              18 :     papszCreateOptions = CSLDuplicate(papszOptions);
    1106                 : 
    1107              18 :     const char* pszFormat = CSLFetchNameValue(papszCreateOptions, "FORMAT");
    1108              18 :     bIsOutputGML3 = pszFormat && EQUAL(pszFormat, "GML3");
    1109              18 :     bIsOutputGML3Deegree = pszFormat && EQUAL(pszFormat, "GML3Deegree");
    1110              18 :     bIsOutputGML32 = pszFormat && EQUAL(pszFormat, "GML3.2");
    1111              18 :     if (bIsOutputGML3Deegree || bIsOutputGML32)
    1112               2 :         bIsOutputGML3 = TRUE;
    1113                 : 
    1114                 :     bIsLongSRSRequired =
    1115              18 :         CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "GML3_LONGSRS", "YES"));
    1116                 : 
    1117                 :     bWriteSpaceIndentation =
    1118              18 :         CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "SPACE_INDENTATION", "YES"));
    1119                 : 
    1120                 : /* -------------------------------------------------------------------- */
    1121                 : /*      Create the output file.                                         */
    1122                 : /* -------------------------------------------------------------------- */
    1123              18 :     pszName = CPLStrdup( pszFilename );
    1124              18 :     osFilename = pszName;
    1125                 : 
    1126              36 :     if( strcmp(pszFilename,"/vsistdout/") == 0 ||
    1127                 :         strncmp(pszFilename,"/vsigzip/", 9) == 0 )
    1128                 :     {
    1129               0 :         fpOutput = VSIFOpenL(pszFilename, "wb");
    1130               0 :         bFpOutputIsNonSeekable = TRUE;
    1131               0 :         bFpOutputSingleFile = TRUE;
    1132                 :     }
    1133              18 :     else if ( strncmp(pszFilename,"/vsizip/", 8) == 0)
    1134                 :     {
    1135               0 :         if (EQUAL(CPLGetExtension(pszFilename), "zip"))
    1136                 :         {
    1137               0 :             CPLFree(pszName);
    1138               0 :             pszName = CPLStrdup(CPLFormFilename(pszFilename, "out.gml", NULL));
    1139                 :         }
    1140                 : 
    1141               0 :         fpOutput = VSIFOpenL(pszName, "wb");
    1142               0 :         bFpOutputIsNonSeekable = TRUE;
    1143                 :     }
    1144                 :     else
    1145              18 :         fpOutput = VSIFOpenL( pszFilename, "wb+" );
    1146              18 :     if( fpOutput == NULL )
    1147                 :     {
    1148                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
    1149                 :                   "Failed to create GML file %s.", 
    1150               0 :                   pszFilename );
    1151               0 :         return FALSE;
    1152                 :     }
    1153                 : 
    1154                 : /* -------------------------------------------------------------------- */
    1155                 : /*      Write out "standard" header.                                    */
    1156                 : /* -------------------------------------------------------------------- */
    1157                 :     PrintLine( fpOutput, "%s", 
    1158              18 :                 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" );
    1159                 : 
    1160              18 :     if (!bFpOutputIsNonSeekable)
    1161              18 :         nSchemaInsertLocation = (int) VSIFTellL( fpOutput );
    1162                 : 
    1163              18 :     const char* pszPrefix = GetAppPrefix();
    1164              18 :     const char* pszTargetNameSpace = CSLFetchNameValueDef(papszOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
    1165                 : 
    1166              18 :     PrintLine( fpOutput, "<%s:FeatureCollection", pszPrefix );
    1167                 : 
    1168              18 :     if (IsGML32Output())
    1169                 :         PrintLine( fpOutput, "%s",
    1170               1 :                 "     gml:id=\"aFeatureCollection\"" );
    1171                 : 
    1172                 : /* -------------------------------------------------------------------- */
    1173                 : /*      Write out schema info if provided in creation options.          */
    1174                 : /* -------------------------------------------------------------------- */
    1175              18 :     const char *pszSchemaURI = CSLFetchNameValue(papszOptions,"XSISCHEMAURI");
    1176              18 :     const char *pszSchemaOpt = CSLFetchNameValue( papszOptions, "XSISCHEMA" );
    1177                 : 
    1178              18 :     if( pszSchemaURI != NULL )
    1179                 :     {
    1180                 :         PrintLine( fpOutput, 
    1181               0 :               "     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
    1182                 :         PrintLine( fpOutput, 
    1183                 :               "     xsi:schemaLocation=\"%s\"", 
    1184               0 :                     pszSchemaURI );
    1185                 :     }
    1186              18 :     else if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
    1187                 :     {
    1188              18 :         char *pszBasename = CPLStrdup(CPLGetBasename( pszName ));
    1189                 : 
    1190                 :         PrintLine( fpOutput, 
    1191              18 :               "     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
    1192                 :         PrintLine( fpOutput, 
    1193                 :               "     xsi:schemaLocation=\"%s %s\"",
    1194                 :                     pszTargetNameSpace,
    1195              18 :                     CPLResetExtension( pszBasename, "xsd" ) );
    1196              18 :         CPLFree( pszBasename );
    1197                 :     }
    1198                 : 
    1199                 :     PrintLine( fpOutput,
    1200              18 :                 "     xmlns:%s=\"%s\"", pszPrefix, pszTargetNameSpace );
    1201              18 :     if (IsGML32Output())
    1202                 :         PrintLine( fpOutput, "%s",
    1203               1 :                 "     xmlns:gml=\"http://www.opengis.net/gml/3.2\">" );
    1204                 :     else
    1205                 :         PrintLine( fpOutput, "%s",
    1206              17 :                     "     xmlns:gml=\"http://www.opengis.net/gml\">" );
    1207                 : 
    1208                 : /* -------------------------------------------------------------------- */
    1209                 : /*      Should we initialize an area to place the boundedBy element?    */
    1210                 : /*      We will need to seek back to fill it in.                        */
    1211                 : /* -------------------------------------------------------------------- */
    1212              18 :     nBoundedByLocation = -1;
    1213              18 :     if( CSLFetchBoolean( papszOptions, "BOUNDEDBY", TRUE ))
    1214                 :     {
    1215              18 :         if (!bFpOutputIsNonSeekable )
    1216                 :         {
    1217              18 :             nBoundedByLocation = (int) VSIFTellL( fpOutput );
    1218                 : 
    1219              18 :             if( nBoundedByLocation != -1 )
    1220              18 :                 PrintLine( fpOutput, "%350s", "" );
    1221                 :         }
    1222                 :         else
    1223                 :         {
    1224               0 :             if (bWriteSpaceIndentation)
    1225               0 :                 VSIFPrintfL( fpOutput, "  ");
    1226               0 :             if (IsGML3Output())
    1227               0 :                 PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
    1228                 :             else
    1229               0 :                 PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
    1230                 :         }
    1231                 :     }
    1232                 : 
    1233              18 :     return TRUE;
    1234                 : }
    1235                 : 
    1236                 : /************************************************************************/
    1237                 : /*                            CreateLayer()                             */
    1238                 : /************************************************************************/
    1239                 : 
    1240                 : OGRLayer *
    1241              18 : OGRGMLDataSource::CreateLayer( const char * pszLayerName,
    1242                 :                                OGRSpatialReference *poSRS,
    1243                 :                                OGRwkbGeometryType eType,
    1244                 :                                char ** papszOptions )
    1245                 : 
    1246                 : {
    1247                 : /* -------------------------------------------------------------------- */
    1248                 : /*      Verify we are in update mode.                                   */
    1249                 : /* -------------------------------------------------------------------- */
    1250              18 :     if( fpOutput == NULL )
    1251                 :     {
    1252                 :         CPLError( CE_Failure, CPLE_NoWriteAccess,
    1253                 :                   "Data source %s opened for read access.\n"
    1254                 :                   "New layer %s cannot be created.\n",
    1255               0 :                   pszName, pszLayerName );
    1256                 : 
    1257               0 :         return NULL;
    1258                 :     }
    1259                 : 
    1260                 : /* -------------------------------------------------------------------- */
    1261                 : /*      Ensure name is safe as an element name.                         */
    1262                 : /* -------------------------------------------------------------------- */
    1263              18 :     char *pszCleanLayerName = CPLStrdup( pszLayerName );
    1264                 : 
    1265              18 :     CPLCleanXMLElementName( pszCleanLayerName );
    1266              18 :     if( strcmp(pszCleanLayerName,pszLayerName) != 0 )
    1267                 :     {
    1268                 :         CPLError( CE_Warning, CPLE_AppDefined, 
    1269                 :                   "Layer name '%s' adjusted to '%s' for XML validity.",
    1270               0 :                   pszLayerName, pszCleanLayerName );
    1271                 :     }
    1272                 : 
    1273                 : /* -------------------------------------------------------------------- */
    1274                 : /*      Set or check validity of global SRS.                            */
    1275                 : /* -------------------------------------------------------------------- */
    1276              18 :     if (nLayers == 0)
    1277                 :     {
    1278              18 :         if (poSRS)
    1279               8 :             poGlobalSRS = poSRS->Clone();
    1280                 :     }
    1281                 :     else
    1282                 :     {
    1283               0 :         if (poSRS == NULL ||
    1284                 :             (poGlobalSRS != NULL && poSRS->IsSame(poGlobalSRS)))
    1285                 :         {
    1286               0 :             delete poGlobalSRS;
    1287               0 :             poGlobalSRS = NULL;
    1288                 :         }
    1289                 :     }
    1290                 : 
    1291                 : /* -------------------------------------------------------------------- */
    1292                 : /*      Create the layer object.                                        */
    1293                 : /* -------------------------------------------------------------------- */
    1294                 :     OGRGMLLayer *poLayer;
    1295                 : 
    1296              18 :     poLayer = new OGRGMLLayer( pszCleanLayerName, poSRS, TRUE, eType, this );
    1297                 : 
    1298              18 :     CPLFree( pszCleanLayerName );
    1299                 : 
    1300                 : /* -------------------------------------------------------------------- */
    1301                 : /*      Add layer to data source layer list.                            */
    1302                 : /* -------------------------------------------------------------------- */
    1303                 :     papoLayers = (OGRGMLLayer **)
    1304              18 :         CPLRealloc( papoLayers,  sizeof(OGRGMLLayer *) * (nLayers+1) );
    1305                 :     
    1306              18 :     papoLayers[nLayers++] = poLayer;
    1307                 : 
    1308              18 :     return poLayer;
    1309                 : }
    1310                 : 
    1311                 : /************************************************************************/
    1312                 : /*                           TestCapability()                           */
    1313                 : /************************************************************************/
    1314                 : 
    1315              12 : int OGRGMLDataSource::TestCapability( const char * pszCap )
    1316                 : 
    1317                 : {
    1318              12 :     if( EQUAL(pszCap,ODsCCreateLayer) )
    1319              11 :         return TRUE;
    1320                 :     else
    1321               1 :         return FALSE;
    1322                 : }
    1323                 : 
    1324                 : /************************************************************************/
    1325                 : /*                              GetLayer()                              */
    1326                 : /************************************************************************/
    1327                 : 
    1328             176 : OGRLayer *OGRGMLDataSource::GetLayer( int iLayer )
    1329                 : 
    1330                 : {
    1331             176 :     if( iLayer < 0 || iLayer >= nLayers )
    1332               2 :         return NULL;
    1333                 :     else
    1334             174 :         return papoLayers[iLayer];
    1335                 : }
    1336                 : 
    1337                 : /************************************************************************/
    1338                 : /*                            GrowExtents()                             */
    1339                 : /************************************************************************/
    1340                 : 
    1341              42 : void OGRGMLDataSource::GrowExtents( OGREnvelope3D *psGeomBounds, int nCoordDimension )
    1342                 : 
    1343                 : {
    1344              42 :     sBoundingRect.Merge( *psGeomBounds );
    1345              42 :     if (nCoordDimension == 3)
    1346              20 :         bBBOX3D = TRUE;
    1347              42 : }
    1348                 : 
    1349                 : /************************************************************************/
    1350                 : /*                            InsertHeader()                            */
    1351                 : /*                                                                      */
    1352                 : /*      This method is used to update boundedby info for a              */
    1353                 : /*      dataset, and insert schema descriptions depending on            */
    1354                 : /*      selection options in effect.                                    */
    1355                 : /************************************************************************/
    1356                 : 
    1357              18 : void OGRGMLDataSource::InsertHeader()
    1358                 : 
    1359                 : {
    1360                 :     VSILFILE        *fpSchema;
    1361              18 :     int         nSchemaStart = 0;
    1362                 : 
    1363              18 :     if( bFpOutputSingleFile )
    1364               0 :         return;
    1365                 : 
    1366                 : /* -------------------------------------------------------------------- */
    1367                 : /*      Do we want to write the schema within the GML instance doc      */
    1368                 : /*      or to a separate file?  For now we only support external.       */
    1369                 : /* -------------------------------------------------------------------- */
    1370                 :     const char *pszSchemaURI = CSLFetchNameValue(papszCreateOptions,
    1371              18 :                                                  "XSISCHEMAURI");
    1372                 :     const char *pszSchemaOpt = CSLFetchNameValue( papszCreateOptions, 
    1373              18 :                                                   "XSISCHEMA" );
    1374                 : 
    1375              18 :     if( pszSchemaURI != NULL )
    1376               0 :         return;
    1377                 : 
    1378              36 :     if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
    1379                 :     {
    1380              18 :         const char *pszXSDFilename = CPLResetExtension( pszName, "xsd" );
    1381                 : 
    1382              18 :         fpSchema = VSIFOpenL( pszXSDFilename, "wt" );
    1383              18 :         if( fpSchema == NULL )
    1384                 :         {
    1385                 :             CPLError( CE_Failure, CPLE_OpenFailed, 
    1386                 :                       "Failed to open file %.500s for schema output.", 
    1387               0 :                       pszXSDFilename );
    1388               0 :             return;
    1389                 :         }
    1390              18 :         PrintLine( fpSchema, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );
    1391                 :     }
    1392               0 :     else if( EQUAL(pszSchemaOpt,"INTERNAL") )
    1393                 :     {
    1394               0 :         if (fpOutput == NULL)
    1395               0 :             return;
    1396               0 :         nSchemaStart = (int) VSIFTellL( fpOutput );
    1397               0 :         fpSchema = fpOutput;
    1398                 :     }
    1399                 :     else                                                               
    1400               0 :         return;
    1401                 : 
    1402                 : /* ==================================================================== */
    1403                 : /*      Write the schema section at the end of the file.  Once          */
    1404                 : /*      complete, we will read it back in, and then move the whole      */
    1405                 : /*      file "down" enough to insert the schema at the beginning.       */
    1406                 : /* ==================================================================== */
    1407                 : 
    1408                 : /* -------------------------------------------------------------------- */
    1409                 : /*      Emit the start of the schema section.                           */
    1410                 : /* -------------------------------------------------------------------- */
    1411              18 :     const char* pszPrefix = GetAppPrefix();
    1412              18 :     const char* pszTargetNameSpace = CSLFetchNameValueDef(papszCreateOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
    1413                 : 
    1414              18 :     if (IsGML3Output())
    1415                 :     {
    1416                 :         PrintLine( fpSchema,
    1417               4 :                     "<xs:schema ");
    1418                 :         PrintLine( fpSchema,
    1419               4 :                    "    targetNamespace=\"%s\"", pszTargetNameSpace );
    1420                 :         PrintLine( fpSchema,
    1421                 :                    "    xmlns:%s=\"%s\"",
    1422               4 :                     pszPrefix, pszTargetNameSpace );
    1423                 :         PrintLine( fpSchema,
    1424               4 :                    "    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"");
    1425               4 :         if (IsGML32Output())
    1426                 :         {
    1427                 :             PrintLine( fpSchema,
    1428               1 :                    "    xmlns:gml=\"http://www.opengis.net/gml/3.2\"");
    1429                 :             PrintLine( fpSchema,
    1430               1 :                     "    xmlns:gmlsf=\"http://www.opengis.net/gmlsf/2.0\"");
    1431                 :         }
    1432                 :         else
    1433                 :         {
    1434                 :             PrintLine( fpSchema,
    1435               3 :                     "    xmlns:gml=\"http://www.opengis.net/gml\"");
    1436               3 :             if (!IsGML3DeegreeOutput())
    1437                 :             {
    1438                 :                 PrintLine( fpSchema,
    1439               2 :                         "    xmlns:gmlsf=\"http://www.opengis.net/gmlsf\"");
    1440                 :             }
    1441                 :         }
    1442                 :         PrintLine( fpSchema,
    1443               4 :                    "    elementFormDefault=\"qualified\"");
    1444                 :         PrintLine( fpSchema,
    1445               4 :                    "    version=\"1.0\">");
    1446                 : 
    1447               4 :         if (IsGML32Output())
    1448                 :         {
    1449                 :             PrintLine( fpSchema,
    1450               1 :                     "<xs:annotation>");
    1451                 :             PrintLine( fpSchema,
    1452               1 :                     "  <xs:appinfo source=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\">");
    1453                 :             PrintLine( fpSchema,
    1454               1 :                     "    <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
    1455                 :             PrintLine( fpSchema,
    1456               1 :                     "  </xs:appinfo>");
    1457                 :             PrintLine( fpSchema,
    1458               1 :                     "</xs:annotation>");
    1459                 : 
    1460                 :             PrintLine( fpSchema,
    1461               1 :                         "<xs:import namespace=\"http://www.opengis.net/gml/3.2\" schemaLocation=\"http://schemas.opengis.net/gml/3.2.1/gml.xsd\"/>" );
    1462                 :             PrintLine( fpSchema,
    1463               1 :                         "<xs:import namespace=\"http://www.opengis.net/gmlsf/2.0\" schemaLocation=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\"/>" );
    1464                 :         }
    1465                 :         else
    1466                 :         {
    1467               3 :             if (!IsGML3DeegreeOutput())
    1468                 :             {
    1469                 :                 PrintLine( fpSchema,
    1470               2 :                         "<xs:annotation>");
    1471                 :                 PrintLine( fpSchema,
    1472               2 :                         "  <xs:appinfo source=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\">");
    1473                 :                 PrintLine( fpSchema,
    1474               2 :                         "    <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
    1475                 :                 PrintLine( fpSchema,
    1476               2 :                         "    <gmlsf:GMLProfileSchema>http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd</gmlsf:GMLProfileSchema>");
    1477                 :                 PrintLine( fpSchema,
    1478               2 :                         "  </xs:appinfo>");
    1479                 :                 PrintLine( fpSchema,
    1480               2 :                         "</xs:annotation>");
    1481                 :             }
    1482                 : 
    1483                 :             PrintLine( fpSchema,
    1484               3 :                         "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/>" );
    1485               3 :             if (!IsGML3DeegreeOutput())
    1486                 :             {
    1487                 :                 PrintLine( fpSchema,
    1488               2 :                             "<xs:import namespace=\"http://www.opengis.net/gmlsf\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\"/>" );
    1489                 :             }
    1490                 :         }
    1491                 :     }
    1492                 :     else
    1493                 :     {
    1494                 :         PrintLine( fpSchema,
    1495                 :                     "<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\">",
    1496              14 :                     pszTargetNameSpace, pszPrefix, pszTargetNameSpace );
    1497                 : 
    1498                 :         PrintLine( fpSchema,
    1499              14 :                     "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/>" );
    1500                 :     }
    1501                 : 
    1502                 : /* -------------------------------------------------------------------- */
    1503                 : /*      Define the FeatureCollection                                    */
    1504                 : /* -------------------------------------------------------------------- */
    1505              18 :     if (IsGML3Output())
    1506                 :     {
    1507               4 :         if (IsGML32Output())
    1508                 :         {
    1509                 :             PrintLine( fpSchema,
    1510                 :                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:AbstractGML\"/>",
    1511               1 :                         pszPrefix );
    1512                 :         }
    1513               3 :         else if (IsGML3DeegreeOutput())
    1514                 :         {
    1515                 :             PrintLine( fpSchema,
    1516                 :                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
    1517               1 :                         pszPrefix );
    1518                 :         }
    1519                 :         else
    1520                 :         {
    1521                 :             PrintLine( fpSchema,
    1522                 :                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_GML\"/>",
    1523               2 :                         pszPrefix );
    1524                 :         }
    1525                 : 
    1526               4 :         PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
    1527               4 :         PrintLine( fpSchema, "  <xs:complexContent>" );
    1528               4 :         if (IsGML3DeegreeOutput())
    1529                 :         {
    1530               1 :             PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
    1531               1 :             PrintLine( fpSchema, "      <xs:sequence>" );
    1532               1 :             PrintLine( fpSchema, "        <xs:element name=\"featureMember\" minOccurs=\"0\" maxOccurs=\"unbounded\">" );
    1533                 :         }
    1534                 :         else
    1535                 :         {
    1536               3 :             PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureType\">" );
    1537               3 :             PrintLine( fpSchema, "      <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" );
    1538               3 :             PrintLine( fpSchema, "        <xs:element name=\"featureMember\">" );
    1539                 :         }
    1540               4 :         PrintLine( fpSchema, "          <xs:complexType>" );
    1541               4 :         if (IsGML32Output())
    1542                 :         {
    1543               1 :             PrintLine( fpSchema, "            <xs:complexContent>" );
    1544               1 :             PrintLine( fpSchema, "              <xs:extension base=\"gml:AbstractFeatureMemberType\">" );
    1545               1 :             PrintLine( fpSchema, "                <xs:sequence>" );
    1546               1 :             PrintLine( fpSchema, "                  <xs:element ref=\"gml:AbstractFeature\"/>" );
    1547               1 :             PrintLine( fpSchema, "                </xs:sequence>" );
    1548               1 :             PrintLine( fpSchema, "              </xs:extension>" );
    1549               1 :             PrintLine( fpSchema, "            </xs:complexContent>" );
    1550                 :         }
    1551                 :         else
    1552                 :         {
    1553               3 :             PrintLine( fpSchema, "            <xs:sequence>" );
    1554               3 :             PrintLine( fpSchema, "              <xs:element ref=\"gml:_Feature\"/>" );
    1555               3 :             PrintLine( fpSchema, "            </xs:sequence>" );
    1556                 :         }
    1557               4 :         PrintLine( fpSchema, "          </xs:complexType>" );
    1558               4 :         PrintLine( fpSchema, "        </xs:element>" );
    1559               4 :         PrintLine( fpSchema, "      </xs:sequence>" );
    1560               4 :         PrintLine( fpSchema, "    </xs:extension>" );
    1561               4 :         PrintLine( fpSchema, "  </xs:complexContent>" );
    1562               4 :         PrintLine( fpSchema, "</xs:complexType>" );
    1563                 :     }
    1564                 :     else
    1565                 :     {
    1566                 :         PrintLine( fpSchema,
    1567                 :                     "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
    1568              14 :                     pszPrefix );
    1569                 : 
    1570              14 :         PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
    1571              14 :         PrintLine( fpSchema, "  <xs:complexContent>" );
    1572              14 :         PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
    1573              14 :         PrintLine( fpSchema, "      <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>" );
    1574              14 :         PrintLine( fpSchema, "      <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>" );
    1575              14 :         PrintLine( fpSchema, "    </xs:extension>" );
    1576              14 :         PrintLine( fpSchema, "  </xs:complexContent>" );
    1577              14 :         PrintLine( fpSchema, "</xs:complexType>" );
    1578                 :     }
    1579                 : 
    1580                 : /* ==================================================================== */
    1581                 : /*      Define the schema for each layer.                               */
    1582                 : /* ==================================================================== */
    1583                 :     int iLayer;
    1584                 : 
    1585              36 :     for( iLayer = 0; iLayer < GetLayerCount(); iLayer++ )
    1586                 :     {
    1587              18 :         OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn();
    1588                 :         
    1589                 : /* -------------------------------------------------------------------- */
    1590                 : /*      Emit initial stuff for a feature type.                          */
    1591                 : /* -------------------------------------------------------------------- */
    1592              18 :         if (IsGML32Output())
    1593                 :         {
    1594                 :             PrintLine(
    1595                 :                 fpSchema,
    1596                 :                 "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:AbstractFeature\"/>",
    1597               1 :                 poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
    1598                 :         }
    1599                 :         else
    1600                 :         {
    1601                 :             PrintLine(
    1602                 :                 fpSchema,
    1603                 :                 "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:_Feature\"/>",
    1604              17 :                 poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
    1605                 :         }
    1606                 : 
    1607              18 :         PrintLine( fpSchema, "<xs:complexType name=\"%s_Type\">", poFDefn->GetName());
    1608              18 :         PrintLine( fpSchema, "  <xs:complexContent>");
    1609              18 :         PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureType\">");
    1610              18 :         PrintLine( fpSchema, "      <xs:sequence>");
    1611                 : 
    1612                 : /* -------------------------------------------------------------------- */
    1613                 : /*      Define the geometry attribute.                                  */
    1614                 : /* -------------------------------------------------------------------- */
    1615              18 :         const char* pszGeometryTypeName = "GeometryPropertyType";
    1616              18 :         switch(wkbFlatten(poFDefn->GetGeomType()))
    1617                 :         {
    1618                 :             case wkbPoint:
    1619               6 :                 pszGeometryTypeName = "PointPropertyType";
    1620               6 :                 break;
    1621                 :             case wkbLineString:
    1622               0 :                 if (IsGML3Output())
    1623               0 :                     pszGeometryTypeName = "CurvePropertyType";
    1624                 :                 else
    1625               0 :                     pszGeometryTypeName = "LineStringPropertyType";
    1626               0 :                 break;
    1627                 :             case wkbPolygon:
    1628               2 :                 if (IsGML3Output())
    1629               1 :                     pszGeometryTypeName = "SurfacePropertyType";
    1630                 :                 else
    1631               1 :                     pszGeometryTypeName = "PolygonPropertyType";
    1632               2 :                 break;
    1633                 :             case wkbMultiPoint:
    1634               0 :                 pszGeometryTypeName = "MultiPointPropertyType";
    1635               0 :                 break;
    1636                 :             case wkbMultiLineString:
    1637               1 :                 if (IsGML3Output())
    1638               0 :                     pszGeometryTypeName = "MultiCurvePropertyType";
    1639                 :                 else
    1640               1 :                     pszGeometryTypeName = "MultiLineStringPropertyType";
    1641               1 :                 break;
    1642                 :             case wkbMultiPolygon:
    1643               1 :                 if (IsGML3Output())
    1644               0 :                     pszGeometryTypeName = "MultiSurfacePropertyType";
    1645                 :                 else
    1646               1 :                     pszGeometryTypeName = "MultiPolygonPropertyType";
    1647               1 :                 break;
    1648                 :             case wkbGeometryCollection:
    1649               0 :                 pszGeometryTypeName = "MultiGeometryPropertyType";
    1650                 :                 break;
    1651                 :             default:
    1652                 :                 break;
    1653                 :         }
    1654                 : 
    1655              18 :         if (poFDefn->GetGeomType() != wkbNone)
    1656                 :         {
    1657                 :             PrintLine( fpSchema,
    1658              17 :                 "        <xs:element name=\"geometryProperty\" type=\"gml:%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\"/>", pszGeometryTypeName );
    1659                 :         }
    1660                 :             
    1661                 : /* -------------------------------------------------------------------- */
    1662                 : /*      Emit each of the attributes.                                    */
    1663                 : /* -------------------------------------------------------------------- */
    1664              46 :         for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
    1665                 :         {
    1666              28 :             OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
    1667                 : 
    1668              28 :             if( poFieldDefn->GetType() == OFTInteger )
    1669                 :             {
    1670                 :                 int nWidth;
    1671                 : 
    1672               3 :                 if( poFieldDefn->GetWidth() > 0 )
    1673               2 :                     nWidth = poFieldDefn->GetWidth();
    1674                 :                 else
    1675               1 :                     nWidth = 16;
    1676                 : 
    1677               3 :                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
    1678               3 :                 PrintLine( fpSchema, "          <xs:simpleType>");
    1679               3 :                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:integer\">");
    1680               3 :                 PrintLine( fpSchema, "              <xs:totalDigits value=\"%d\"/>", nWidth);
    1681               3 :                 PrintLine( fpSchema, "            </xs:restriction>");
    1682               3 :                 PrintLine( fpSchema, "          </xs:simpleType>");
    1683               3 :                 PrintLine( fpSchema, "        </xs:element>");
    1684                 :             }
    1685              25 :             else if( poFieldDefn->GetType() == OFTReal )
    1686                 :             {
    1687                 :                 int nWidth, nDecimals;
    1688                 : 
    1689              14 :                 nWidth = poFieldDefn->GetWidth();
    1690              14 :                 nDecimals = poFieldDefn->GetPrecision();
    1691                 : 
    1692              14 :                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">",  poFieldDefn->GetNameRef());
    1693              14 :                 PrintLine( fpSchema, "          <xs:simpleType>");
    1694              14 :                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:decimal\">");
    1695              14 :                 if (nWidth > 0)
    1696                 :                 {
    1697              12 :                     PrintLine( fpSchema, "              <xs:totalDigits value=\"%d\"/>", nWidth);
    1698              12 :                     PrintLine( fpSchema, "              <xs:fractionDigits value=\"%d\"/>", nDecimals);
    1699                 :                 }
    1700              14 :                 PrintLine( fpSchema, "            </xs:restriction>");
    1701              14 :                 PrintLine( fpSchema, "          </xs:simpleType>");
    1702              14 :                 PrintLine( fpSchema, "        </xs:element>");
    1703                 :             }
    1704              11 :             else if( poFieldDefn->GetType() == OFTString )
    1705                 :             {
    1706              10 :                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">",  poFieldDefn->GetNameRef());
    1707              10 :                 PrintLine( fpSchema, "          <xs:simpleType>");
    1708              10 :                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:string\">");
    1709              10 :                 if( poFieldDefn->GetWidth() != 0 )
    1710                 :                 {
    1711               4 :                     PrintLine( fpSchema, "              <xs:maxLength value=\"%d\"/>", poFieldDefn->GetWidth());
    1712                 :                 }
    1713              10 :                 PrintLine( fpSchema, "            </xs:restriction>");
    1714              10 :                 PrintLine( fpSchema, "          </xs:simpleType>");
    1715              10 :                 PrintLine( fpSchema, "        </xs:element>");
    1716                 :             }
    1717               1 :             else if( poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime )
    1718                 :             {
    1719               1 :                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">",  poFieldDefn->GetNameRef());
    1720               1 :                 PrintLine( fpSchema, "          <xs:simpleType>");
    1721               1 :                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:string\">");
    1722               1 :                 PrintLine( fpSchema, "            </xs:restriction>");
    1723               1 :                 PrintLine( fpSchema, "          </xs:simpleType>");
    1724               1 :                 PrintLine( fpSchema, "        </xs:element>");
    1725                 :             }
    1726                 :             else
    1727                 :             {
    1728                 :                 /* TODO */
    1729                 :             }
    1730                 :         } /* next field */
    1731                 : 
    1732                 : /* -------------------------------------------------------------------- */
    1733                 : /*      Finish off feature type.                                        */
    1734                 : /* -------------------------------------------------------------------- */
    1735              18 :         PrintLine( fpSchema, "      </xs:sequence>");
    1736              18 :         PrintLine( fpSchema, "    </xs:extension>");
    1737              18 :         PrintLine( fpSchema, "  </xs:complexContent>");
    1738              18 :         PrintLine( fpSchema, "</xs:complexType>" );
    1739                 :     } /* next layer */
    1740                 : 
    1741              18 :     PrintLine( fpSchema, "</xs:schema>" );
    1742                 : 
    1743                 : /* ==================================================================== */
    1744                 : /*      Move schema to the start of the file.                           */
    1745                 : /* ==================================================================== */
    1746              18 :     if( fpSchema == fpOutput )
    1747                 :     {
    1748                 : /* -------------------------------------------------------------------- */
    1749                 : /*      Read the schema into memory.                                    */
    1750                 : /* -------------------------------------------------------------------- */
    1751               0 :         int nSchemaSize = (int) VSIFTellL( fpOutput ) - nSchemaStart;
    1752               0 :         char *pszSchema = (char *) CPLMalloc(nSchemaSize+1);
    1753                 :     
    1754               0 :         VSIFSeekL( fpOutput, nSchemaStart, SEEK_SET );
    1755                 : 
    1756               0 :         VSIFReadL( pszSchema, 1, nSchemaSize, fpOutput );
    1757               0 :         pszSchema[nSchemaSize] = '\0';
    1758                 :     
    1759                 : /* -------------------------------------------------------------------- */
    1760                 : /*      Move file data down by "schema size" bytes from after <?xml>    */
    1761                 : /*      header so we have room insert the schema.  Move in pretty       */
    1762                 : /*      big chunks.                                                     */
    1763                 : /* -------------------------------------------------------------------- */
    1764               0 :         int nChunkSize = MIN(nSchemaStart-nSchemaInsertLocation,250000);
    1765               0 :         char *pszChunk = (char *) CPLMalloc(nChunkSize);
    1766               0 :         int nEndOfUnmovedData = nSchemaStart;
    1767                 : 
    1768               0 :         for( nEndOfUnmovedData = nSchemaStart;
    1769                 :              nEndOfUnmovedData > nSchemaInsertLocation; )
    1770                 :         {
    1771                 :             int nBytesToMove = 
    1772               0 :                 MIN(nChunkSize, nEndOfUnmovedData - nSchemaInsertLocation );
    1773                 : 
    1774               0 :             VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove, SEEK_SET );
    1775               0 :             VSIFReadL( pszChunk, 1, nBytesToMove, fpOutput );
    1776                 :             VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove + nSchemaSize, 
    1777               0 :                       SEEK_SET );
    1778               0 :             VSIFWriteL( pszChunk, 1, nBytesToMove, fpOutput );
    1779                 :         
    1780               0 :             nEndOfUnmovedData -= nBytesToMove;
    1781                 :         }
    1782                 : 
    1783               0 :         CPLFree( pszChunk );
    1784                 : 
    1785                 : /* -------------------------------------------------------------------- */
    1786                 : /*      Write the schema in the opened slot.                            */
    1787                 : /* -------------------------------------------------------------------- */
    1788               0 :         VSIFSeekL( fpOutput, nSchemaInsertLocation, SEEK_SET );
    1789               0 :         VSIFWriteL( pszSchema, 1, nSchemaSize, fpOutput );
    1790                 : 
    1791               0 :         VSIFSeekL( fpOutput, 0, SEEK_END );
    1792                 : 
    1793               0 :         nBoundedByLocation += nSchemaSize;
    1794                 : 
    1795               0 :         CPLFree(pszSchema);
    1796                 :     }
    1797                 : /* -------------------------------------------------------------------- */
    1798                 : /*      Close external schema files.                                    */
    1799                 : /* -------------------------------------------------------------------- */
    1800                 :     else
    1801              18 :         VSIFCloseL( fpSchema );
    1802                 : }
    1803                 : 
    1804                 : 
    1805                 : /************************************************************************/
    1806                 : /*                            PrintLine()                               */
    1807                 : /************************************************************************/
    1808                 : 
    1809            1256 : void OGRGMLDataSource::PrintLine(VSILFILE* fp, const char *fmt, ...)
    1810                 : {
    1811            1256 :     CPLString osWork;
    1812                 :     va_list args;
    1813                 : 
    1814            1256 :     va_start( args, fmt );
    1815            1256 :     osWork.vPrintf( fmt, args );
    1816            1256 :     va_end( args );
    1817                 : 
    1818                 : #ifdef WIN32
    1819                 :     const char* pszEOL = "\r\n";
    1820                 : #else
    1821            1256 :     const char* pszEOL = "\n";
    1822                 : #endif
    1823                 : 
    1824            1256 :     VSIFPrintfL(fp, "%s%s", osWork.c_str(), pszEOL);
    1825            1256 : }
    1826                 : 
    1827                 : 
    1828                 : /************************************************************************/
    1829                 : /*                     OGRGMLSingleFeatureLayer                         */
    1830                 : /************************************************************************/
    1831                 : 
    1832                 : class OGRGMLSingleFeatureLayer : public OGRLayer
    1833                 : {
    1834                 :   private:
    1835                 :     int                 nVal;
    1836                 :     OGRFeatureDefn     *poFeatureDefn;
    1837                 :     int                 iNextShapeId;
    1838                 : 
    1839                 :   public:
    1840                 :                         OGRGMLSingleFeatureLayer(int nVal );
    1841               1 :                         ~OGRGMLSingleFeatureLayer() { poFeatureDefn->Release(); }
    1842                 : 
    1843               0 :     virtual void        ResetReading() { iNextShapeId = 0; }
    1844                 :     virtual OGRFeature *GetNextFeature();
    1845               0 :     virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; }
    1846               0 :     virtual int         TestCapability( const char * ) { return FALSE; }
    1847                 : };
    1848                 : 
    1849                 : /************************************************************************/
    1850                 : /*                      OGRGMLSingleFeatureLayer()                      */
    1851                 : /************************************************************************/
    1852                 : 
    1853               1 : OGRGMLSingleFeatureLayer::OGRGMLSingleFeatureLayer( int nVal )
    1854                 : {
    1855               1 :     poFeatureDefn = new OGRFeatureDefn( "SELECT" );
    1856               1 :     poFeatureDefn->Reference();
    1857               1 :     OGRFieldDefn oField( "Validates", OFTInteger );
    1858               1 :     poFeatureDefn->AddFieldDefn( &oField );
    1859                 : 
    1860               1 :     this->nVal = nVal;
    1861               1 :     iNextShapeId = 0;
    1862               1 : }
    1863                 : 
    1864                 : /************************************************************************/
    1865                 : /*                           GetNextFeature()                           */
    1866                 : /************************************************************************/
    1867                 : 
    1868               1 : OGRFeature * OGRGMLSingleFeatureLayer::GetNextFeature()
    1869                 : {
    1870               1 :     if (iNextShapeId != 0)
    1871               0 :         return NULL;
    1872                 : 
    1873               1 :     OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
    1874               1 :     poFeature->SetField(0, nVal);
    1875               1 :     poFeature->SetFID(iNextShapeId ++);
    1876               1 :     return poFeature;
    1877                 : }
    1878                 : 
    1879                 : /************************************************************************/
    1880                 : /*                            ExecuteSQL()                              */
    1881                 : /************************************************************************/
    1882                 : 
    1883               5 : OGRLayer * OGRGMLDataSource::ExecuteSQL( const char *pszSQLCommand,
    1884                 :                                          OGRGeometry *poSpatialFilter,
    1885                 :                                          const char *pszDialect )
    1886                 : {
    1887               5 :     if (poReader != NULL && EQUAL(pszSQLCommand, "SELECT ValidateSchema()"))
    1888                 :     {
    1889               1 :         int bIsValid = FALSE;
    1890               1 :         if (osXSDFilename.size())
    1891                 :         {
    1892               1 :             CPLErrorReset();
    1893               1 :             bIsValid = CPLValidateXML(osFilename, osXSDFilename, NULL);
    1894                 :         }
    1895               1 :         return new OGRGMLSingleFeatureLayer(bIsValid);
    1896                 :     }
    1897                 : 
    1898               4 :     return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
    1899                 : }
    1900                 : 
    1901                 : /************************************************************************/
    1902                 : /*                          ReleaseResultSet()                          */
    1903                 : /************************************************************************/
    1904                 : 
    1905               4 : void OGRGMLDataSource::ReleaseResultSet( OGRLayer * poResultsSet )
    1906                 : {
    1907               4 :     delete poResultsSet;
    1908               4 : }
    1909                 : 
    1910                 : /************************************************************************/
    1911                 : /*                         FindAndParseBoundedBy()                      */
    1912                 : /************************************************************************/
    1913                 : 
    1914             115 : void OGRGMLDataSource::FindAndParseBoundedBy(VSILFILE* fp)
    1915                 : {
    1916                 :     /* Build a shortened XML file that contain only the global */
    1917                 :     /* boundedBy element, so as to be able to parse it easily */
    1918                 : 
    1919                 :     char szStartTag[128];
    1920             115 :     char* pszXML = (char*)CPLMalloc(8192 + 128 + 3 + 1);
    1921             115 :     VSIFSeekL(fp, 0, SEEK_SET);
    1922             115 :     int nRead = (int)VSIFReadL(pszXML, 1, 8192, fp);
    1923             115 :     pszXML[nRead] = 0;
    1924                 : 
    1925             115 :     const char* pszStartTag = strchr(pszXML, '<');
    1926             115 :     if (pszStartTag != NULL)
    1927                 :     {
    1928             338 :         while (pszStartTag != NULL && pszStartTag[1] == '?')
    1929             108 :             pszStartTag = strchr(pszStartTag + 1, '<');
    1930                 : 
    1931             115 :         if (pszStartTag != NULL)
    1932                 :         {
    1933             115 :             pszStartTag ++;
    1934             115 :             const char* pszEndTag = strchr(pszStartTag, ' ');
    1935             230 :             if (pszEndTag != NULL && pszEndTag - pszStartTag < 128 )
    1936                 :             {
    1937             115 :                 memcpy(szStartTag, pszStartTag, pszEndTag - pszStartTag);
    1938             115 :                 szStartTag[pszEndTag - pszStartTag] = '\0';
    1939                 :             }
    1940                 :             else
    1941               0 :                 pszStartTag = NULL;
    1942                 :         }
    1943                 :     }
    1944                 : 
    1945             115 :     char* pszEndBoundedBy = strstr(pszXML, "</wfs:boundedBy>");
    1946             115 :     int bWFSBoundedBy = FALSE;
    1947             115 :     if (pszEndBoundedBy != NULL)
    1948               2 :         bWFSBoundedBy = TRUE;
    1949                 :     else
    1950             113 :         pszEndBoundedBy = strstr(pszXML, "</gml:boundedBy>");
    1951             115 :     if (pszStartTag != NULL && pszEndBoundedBy != NULL)
    1952                 :     {
    1953              93 :         const char* pszSRSName = NULL;
    1954                 :         char szSRSName[128];
    1955                 : 
    1956              93 :         szSRSName[0] = '\0';
    1957                 : 
    1958                 :         /* Find a srsName somewhere for some WFS 2.0 documents */
    1959                 :         /* that have not it set at the <wfs:boundedBy> element */
    1960                 :         /* e.g. http://geoserv.weichand.de:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAME=bvv:gmd_ex */
    1961              93 :         if( bIsWFS )
    1962                 :         {
    1963              54 :             pszSRSName = strstr(pszXML, "srsName=\"");
    1964              54 :             if( pszSRSName != NULL )
    1965                 :             {
    1966              53 :                 pszSRSName += 9;
    1967              53 :                 const char* pszEndQuote = strchr(pszSRSName, '"');
    1968              53 :                 if (pszEndQuote != NULL &&
    1969                 :                     (size_t)(pszEndQuote - pszSRSName) < sizeof(szSRSName))
    1970                 :                 {
    1971              53 :                     memcpy(szSRSName, pszSRSName, pszEndQuote - pszSRSName);
    1972              53 :                     szSRSName[pszEndQuote - pszSRSName] = '\0';
    1973                 :                 }
    1974              53 :                 pszSRSName = NULL;
    1975                 :             }
    1976                 :         }
    1977                 : 
    1978              93 :         pszEndBoundedBy[strlen("</gml:boundedBy>")] = '\0';
    1979              93 :         strcat(pszXML, "</");
    1980              93 :         strcat(pszXML, szStartTag);
    1981              93 :         strcat(pszXML, ">");
    1982                 : 
    1983              93 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    1984              93 :         CPLXMLNode* psXML = CPLParseXMLString(pszXML);
    1985              93 :         CPLPopErrorHandler();
    1986              93 :         CPLErrorReset();
    1987              93 :         if (psXML != NULL)
    1988                 :         {
    1989              92 :             CPLXMLNode* psBoundedBy = NULL;
    1990              92 :             CPLXMLNode* psIter = psXML;
    1991             277 :             while(psIter != NULL)
    1992                 :             {
    1993                 :                 psBoundedBy = CPLGetXMLNode(psIter, bWFSBoundedBy ?
    1994             185 :                                         "wfs:boundedBy" : "gml:boundedBy");
    1995             185 :                 if (psBoundedBy != NULL)
    1996              92 :                     break;
    1997              93 :                 psIter = psIter->psNext;
    1998                 :             }
    1999                 : 
    2000              92 :             const char* pszLowerCorner = NULL;
    2001              92 :             const char* pszUpperCorner = NULL;
    2002              92 :             if (psBoundedBy != NULL)
    2003                 :             {
    2004              92 :                 CPLXMLNode* psEnvelope = CPLGetXMLNode(psBoundedBy, "gml:Envelope");
    2005              92 :                 if (psEnvelope)
    2006                 :                 {
    2007              61 :                     pszSRSName = CPLGetXMLValue(psEnvelope, "srsName", NULL);
    2008              61 :                     pszLowerCorner = CPLGetXMLValue(psEnvelope, "gml:lowerCorner", NULL);
    2009              61 :                     pszUpperCorner = CPLGetXMLValue(psEnvelope, "gml:upperCorner", NULL);
    2010                 :                 }
    2011                 :             }
    2012                 : 
    2013              94 :             if( bIsWFS && pszSRSName == NULL &&
    2014                 :                 pszLowerCorner != NULL && pszUpperCorner != NULL &&
    2015               2 :                 szSRSName[0] != '\0' )
    2016                 :             {
    2017               2 :                 pszSRSName = szSRSName;
    2018                 :             }
    2019                 : 
    2020              92 :             if (pszSRSName != NULL && pszLowerCorner != NULL && pszUpperCorner != NULL)
    2021                 :             {
    2022              60 :                 char** papszLC = CSLTokenizeString(pszLowerCorner);
    2023              60 :                 char** papszUC = CSLTokenizeString(pszUpperCorner);
    2024              60 :                 if (CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2)
    2025                 :                 {
    2026              60 :                     CPLDebug("GML", "Global SRS = %s", pszSRSName);
    2027                 : 
    2028              60 :                     if (strncmp(pszSRSName, "http://www.opengis.net/gml/srs/epsg.xml#", 40) == 0)
    2029                 :                     {
    2030               0 :                         std::string osWork;
    2031               0 :                         osWork.assign("EPSG:", 5);
    2032               0 :                         osWork.append(pszSRSName+40);
    2033               0 :                         poReader->SetGlobalSRSName(osWork.c_str());
    2034                 :                     }
    2035                 :                     else
    2036              60 :                         poReader->SetGlobalSRSName(pszSRSName);
    2037                 : 
    2038              60 :                     double dfMinX = CPLAtofM(papszLC[0]);
    2039              60 :                     double dfMinY = CPLAtofM(papszLC[1]);
    2040              60 :                     double dfMaxX = CPLAtofM(papszUC[0]);
    2041              60 :                     double dfMaxY = CPLAtofM(papszUC[1]);
    2042                 : 
    2043              60 :                     SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY);
    2044                 :                 }
    2045              60 :                 CSLDestroy(papszLC);
    2046              60 :                 CSLDestroy(papszUC);
    2047                 :             }
    2048                 : 
    2049              92 :             CPLDestroyXMLNode(psXML);
    2050                 :         }
    2051                 :     }
    2052                 : 
    2053             115 :     CPLFree(pszXML);
    2054             115 : }
    2055                 : 
    2056                 : /************************************************************************/
    2057                 : /*                             SetExtents()                             */
    2058                 : /************************************************************************/
    2059                 : 
    2060              60 : void OGRGMLDataSource::SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
    2061                 : {
    2062              60 :     sBoundingRect.MinX = dfMinX;
    2063              60 :     sBoundingRect.MinY = dfMinY;
    2064              60 :     sBoundingRect.MaxX = dfMaxX;
    2065              60 :     sBoundingRect.MaxY = dfMaxY;
    2066              60 : }
    2067                 : 
    2068                 : /************************************************************************/
    2069                 : /*                             GetAppPrefix()                           */
    2070                 : /************************************************************************/
    2071                 : 
    2072             107 : const char* OGRGMLDataSource::GetAppPrefix()
    2073                 : {
    2074             107 :     return CSLFetchNameValueDef(papszCreateOptions, "PREFIX", "ogr");
    2075                 : }

Generated by: LCOV version 1.7