LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/gml - ogrgmldatasource.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 904 774 85.6 %
Date: 2012-12-26 Functions: 29 21 72.4 %

       1                 : /******************************************************************************
       2                 :  * $Id: ogrgmldatasource.cpp 24983 2012-09-26 19:48:37Z 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 24983 2012-09-26 19:48:37Z rouault $");
      48                 : 
      49                 : /************************************************************************/
      50                 : /*                   ReplaceSpaceByPct20IfNeeded()                      */
      51                 : /************************************************************************/
      52                 : 
      53              46 : static CPLString ReplaceSpaceByPct20IfNeeded(const char* pszURL)
      54                 : {
      55                 :     /* Replace ' ' by '%20' */
      56              46 :     CPLString osRet = pszURL;
      57              46 :     const char* pszNeedle = strstr(pszURL, "text/xml; subtype=");
      58              46 :     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             542 : OGRGMLDataSource::OGRGMLDataSource()
      76                 : 
      77                 : {
      78             542 :     pszName = NULL;
      79             542 :     papoLayers = NULL;
      80             542 :     nLayers = 0;
      81                 : 
      82             542 :     poReader = NULL;
      83             542 :     fpOutput = NULL;
      84             542 :     bFpOutputIsNonSeekable = FALSE;
      85             542 :     bFpOutputSingleFile = FALSE;
      86             542 :     bIsOutputGML3 = FALSE;
      87             542 :     bIsOutputGML3Deegree = FALSE;
      88             542 :     bIsOutputGML32 = FALSE;
      89             542 :     bIsLongSRSRequired = FALSE;
      90             542 :     bWriteSpaceIndentation = TRUE;
      91                 : 
      92             542 :     papszCreateOptions = NULL;
      93             542 :     bOutIsTempFile = FALSE;
      94                 : 
      95             542 :     bExposeGMLId = FALSE;
      96             542 :     bExposeFid = FALSE;
      97             542 :     nSchemaInsertLocation = -1;
      98             542 :     nBoundedByLocation = -1;
      99             542 :     bBBOX3D = FALSE;
     100                 : 
     101             542 :     poGlobalSRS = NULL;
     102             542 :     bIsWFS = FALSE;
     103                 : 
     104             542 :     eReadMode = STANDARD;
     105             542 :     poStoredGMLFeature = NULL;
     106             542 :     poLastReadLayer = NULL;
     107                 : 
     108             542 :     m_bInvertAxisOrderIfLatLong = FALSE;
     109             542 :     m_bConsiderEPSGAsURN = FALSE;
     110             542 :     m_bGetSecondaryGeometryOption = FALSE;
     111             542 : }
     112                 : 
     113                 : /************************************************************************/
     114                 : /*                        ~OGRGMLDataSource()                         */
     115                 : /************************************************************************/
     116                 : 
     117             542 : OGRGMLDataSource::~OGRGMLDataSource()
     118                 : 
     119                 : {
     120                 : 
     121             542 :     if( fpOutput != NULL )
     122                 :     {
     123              50 :         const char* pszPrefix = GetAppPrefix();
     124                 : 
     125              50 :         PrintLine( fpOutput, "</%s:FeatureCollection>", pszPrefix );
     126                 : 
     127              50 :         if( bFpOutputIsNonSeekable)
     128                 :         {
     129               0 :             VSIFCloseL( fpOutput );
     130               0 :             fpOutput = NULL;
     131                 :         }
     132                 : 
     133              50 :         InsertHeader();
     134                 : 
     135              50 :         if( !bFpOutputIsNonSeekable
     136                 :             && nBoundedByLocation != -1
     137                 :             && VSIFSeekL( fpOutput, nBoundedByLocation, SEEK_SET ) == 0 )
     138                 :         {
     139              50 :             if (sBoundingRect.IsInit()  && IsGML3Output())
     140                 :             {
     141              25 :                 int bCoordSwap = FALSE;
     142                 :                 char* pszSRSName;
     143              25 :                 if (poGlobalSRS)
     144              25 :                     pszSRSName = GML_GetSRSName(poGlobalSRS, IsLongSRSRequired(), &bCoordSwap);
     145                 :                 else
     146               0 :                     pszSRSName = CPLStrdup("");
     147                 :                 char szLowerCorner[75], szUpperCorner[75];
     148              25 :                 if (bCoordSwap)
     149                 :                 {
     150              24 :                     OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinY, sBoundingRect.MinX, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
     151              24 :                     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              25 :                 if (bWriteSpaceIndentation)
     159              25 :                     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              25 :                            (bBBOX3D) ? " srsDimension=\"3\"" : "", pszSRSName, szLowerCorner, szUpperCorner);
     162              25 :                 CPLFree(pszSRSName);
     163                 :             }
     164              25 :             else if (sBoundingRect.IsInit())
     165                 :             {
     166              16 :                 if (bWriteSpaceIndentation)
     167              16 :                     VSIFPrintfL( fpOutput, "  ");
     168              16 :                 PrintLine( fpOutput, "<gml:boundedBy>" );
     169              16 :                 if (bWriteSpaceIndentation)
     170              16 :                     VSIFPrintfL( fpOutput, "    ");
     171              16 :                 PrintLine( fpOutput, "<gml:Box>" );
     172              16 :                 if (bWriteSpaceIndentation)
     173              16 :                     VSIFPrintfL( fpOutput, "      ");
     174                 :                 VSIFPrintfL( fpOutput,
     175                 :                             "<gml:coord><gml:X>%.16g</gml:X>"
     176                 :                             "<gml:Y>%.16g</gml:Y>",
     177              16 :                             sBoundingRect.MinX, sBoundingRect.MinY );
     178              16 :                 if (bBBOX3D)
     179                 :                     VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
     180               1 :                                sBoundingRect.MinZ );
     181              16 :                 PrintLine( fpOutput, "</gml:coord>");
     182              16 :                 if (bWriteSpaceIndentation)
     183              16 :                     VSIFPrintfL( fpOutput, "      ");
     184                 :                 VSIFPrintfL( fpOutput,
     185                 :                             "<gml:coord><gml:X>%.16g</gml:X>"
     186                 :                             "<gml:Y>%.16g</gml:Y>",
     187              16 :                             sBoundingRect.MaxX, sBoundingRect.MaxY );
     188              16 :                 if (bBBOX3D)
     189                 :                     VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
     190               1 :                                sBoundingRect.MaxZ );
     191              16 :                 PrintLine( fpOutput, "</gml:coord>");
     192              16 :                 if (bWriteSpaceIndentation)
     193              16 :                     VSIFPrintfL( fpOutput, "    ");
     194              16 :                 PrintLine( fpOutput, "</gml:Box>" );
     195              16 :                 if (bWriteSpaceIndentation)
     196              16 :                     VSIFPrintfL( fpOutput, "  ");
     197              16 :                 PrintLine( fpOutput, "</gml:boundedBy>" );
     198                 :             }
     199                 :             else
     200                 :             {
     201               9 :                 if (bWriteSpaceIndentation)
     202               9 :                     VSIFPrintfL( fpOutput, "  ");
     203               9 :                 if (IsGML3Output())
     204               3 :                     PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
     205                 :                 else
     206               6 :                     PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
     207                 :             }
     208                 :         }
     209                 : 
     210              50 :         if (fpOutput)
     211              50 :             VSIFCloseL( fpOutput );
     212                 :     }
     213                 : 
     214             542 :     CSLDestroy( papszCreateOptions );
     215             542 :     CPLFree( pszName );
     216                 : 
     217             762 :     for( int i = 0; i < nLayers; i++ )
     218             220 :         delete papoLayers[i];
     219                 :     
     220             542 :     CPLFree( papoLayers );
     221                 : 
     222             542 :     if( poReader )
     223                 :     {
     224             145 :         if (bOutIsTempFile)
     225               0 :             VSIUnlink(poReader->GetSourceFileName());
     226             145 :         delete poReader;
     227                 :     }
     228                 : 
     229             542 :     delete poGlobalSRS;
     230                 : 
     231             542 :     delete poStoredGMLFeature;
     232                 : 
     233             542 :     if (osXSDFilename.compare(CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this)) == 0)
     234               0 :         VSIUnlink(osXSDFilename);
     235             542 : }
     236                 : 
     237                 : /************************************************************************/
     238                 : /*                                Open()                                */
     239                 : /************************************************************************/
     240                 : 
     241             492 : int OGRGMLDataSource::Open( const char * pszNameIn, int bTestOpen )
     242                 : 
     243                 : {
     244                 :     VSILFILE   *fp;
     245                 :     char        szHeader[2048];
     246             492 :     int         nNumberOfFeatures = 0;
     247             492 :     CPLString   osWithVsiGzip;
     248             492 :     const char *pszSchemaLocation = NULL;
     249             492 :     int bCheckAuxFile = TRUE;
     250                 : 
     251                 : /* -------------------------------------------------------------------- */
     252                 : /*      Extract xsd filename from connexion string if present.          */
     253                 : /* -------------------------------------------------------------------- */
     254             492 :     osFilename = pszNameIn;
     255             492 :     const char *pszXSDFilenameTmp = strstr(pszNameIn, ",xsd=");
     256             492 :     if (pszXSDFilenameTmp != NULL)
     257                 :     {
     258              39 :         osFilename.resize(pszXSDFilenameTmp - pszNameIn);
     259              39 :         osXSDFilename = pszXSDFilenameTmp + strlen(",xsd=");
     260                 :     }
     261             492 :     const char *pszFilename = osFilename.c_str();
     262                 : 
     263             492 :     pszName = CPLStrdup( pszNameIn );
     264                 : 
     265                 : /* -------------------------------------------------------------------- */
     266                 : /*      Open the source file.                                           */
     267                 : /* -------------------------------------------------------------------- */
     268             492 :     fp = VSIFOpenL( pszFilename, "r" );
     269             492 :     if( fp == NULL )
     270                 :     {
     271              95 :         if( !bTestOpen )
     272                 :             CPLError( CE_Failure, CPLE_OpenFailed, 
     273                 :                       "Failed to open GML file `%s'.", 
     274               0 :                       pszFilename );
     275                 : 
     276              95 :         return FALSE;
     277                 :     }
     278                 : 
     279             397 :     int bExpatCompatibleEncoding = FALSE;
     280             397 :     int bHas3D = FALSE;
     281             397 :     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             397 :     if( bTestOpen )
     288                 :     {
     289             397 :         size_t nRead = VSIFReadL( szHeader, 1, sizeof(szHeader)-1, fp );
     290             397 :         if (nRead <= 0)
     291                 :         {
     292              11 :             VSIFCloseL( fp );
     293              11 :             return FALSE;
     294                 :         }
     295             386 :         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             386 :         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             386 :         char* szPtr = szHeader;
     331                 : 
     332             388 :         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             386 :         const char* pszEncoding = strstr(szPtr, "encoding=");
     340             386 :         if (pszEncoding)
     341             320 :             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             484 :                                          pszEncoding[20] == pszEncoding[9])) ;
     346                 :         else
     347             222 :             bExpatCompatibleEncoding = TRUE; /* utf-8 is the default */
     348                 : 
     349             386 :         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             386 :         if( szPtr[0] != '<' 
     355                 :             || strstr(szPtr,"opengis.net/gml") == NULL )
     356                 :         {
     357             237 :             VSIFCloseL( fp );
     358             237 :             return FALSE;
     359                 :         }
     360                 : 
     361                 :         /* Ignore GeoRSS documents. They will be recognized by the GeoRSS driver */
     362             149 :         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             145 :         const char* pszFeatureCollection = strstr(szPtr, "wfs:FeatureCollection");
     372             145 :         if (pszFeatureCollection == NULL)
     373              97 :             pszFeatureCollection = strstr(szPtr, "gml:FeatureCollection"); /* GML 3.2.1 output */
     374             145 :         if (pszFeatureCollection == NULL)
     375                 :         {
     376              87 :             pszFeatureCollection = strstr(szPtr, "<FeatureCollection"); /* Deegree WFS 1.0.0 output */
     377              87 :             if (pszFeatureCollection && strstr(szPtr, "xmlns:wfs=\"http://www.opengis.net/wfs\"") == NULL)
     378               1 :                 pszFeatureCollection = NULL;
     379                 :         }
     380             145 :         if (pszFeatureCollection)
     381                 :         {
     382              60 :             bExposeGMLId = TRUE;
     383              60 :             bIsWFS = TRUE;
     384              60 :             const char* pszNumberOfFeatures = strstr(szPtr, "numberOfFeatures=");
     385              60 :             if (pszNumberOfFeatures)
     386                 :             {
     387              36 :                 pszNumberOfFeatures += 17;
     388              36 :                 char ch = pszNumberOfFeatures[0];
     389              36 :                 if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
     390                 :                 {
     391              36 :                     nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
     392                 :                 }
     393                 :             }
     394              24 :             else if ((pszNumberOfFeatures = strstr(szPtr, "numberReturned=")) != NULL) /* WFS 2.0.0 */
     395                 :             {
     396               7 :                 pszNumberOfFeatures += 15;
     397               7 :                 char ch = pszNumberOfFeatures[0];
     398               7 :                 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               7 :                     nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
     404                 :                 }
     405                 :             }
     406                 :         }
     407              85 :         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              85 :                            strstr(szPtr, " gml:id='") != NULL;
     419                 :             bExposeFid = strstr(szPtr, " fid=\"") != NULL ||
     420              85 :                          strstr(szPtr, " fid='") != NULL;
     421                 :             
     422              85 :             const char* pszExposeGMLId = CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL);
     423              85 :             if (pszExposeGMLId)
     424               0 :                 bExposeGMLId = CSLTestBoolean(pszExposeGMLId);
     425                 : 
     426              85 :             const char* pszExposeFid = CPLGetConfigOption("GML_EXPOSE_FID", NULL);
     427              85 :             if (pszExposeFid)
     428               1 :                 bExposeFid = CSLTestBoolean(pszExposeFid);
     429                 :         }
     430                 : 
     431             145 :         bHintConsiderEPSGAsURN = strstr(szPtr, "xmlns:fme=\"http://www.safe.com/gml/fme\"") != NULL;
     432                 : 
     433             145 :         pszSchemaLocation = strstr(szPtr, "schemaLocation=");
     434             145 :         if (pszSchemaLocation)
     435             121 :             pszSchemaLocation += strlen("schemaLocation=");
     436                 : 
     437             145 :         if (bIsWFS && EQUALN(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")))
     438              36 :             bCheckAuxFile = FALSE;
     439                 :     }
     440                 : 
     441                 : /* -------------------------------------------------------------------- */
     442                 : /*      We assume now that it is GML.  Instantiate a GMLReader on it.   */
     443                 : /* -------------------------------------------------------------------- */
     444                 : 
     445             145 :     const char* pszReadMode = CPLGetConfigOption("GML_READ_MODE", NULL);
     446             287 :     if (pszReadMode == NULL || EQUAL(pszReadMode, "STANDARD"))
     447             142 :         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             145 :         CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES"));
     459                 : 
     460                 :     const char* pszConsiderEPSGAsURN =
     461             145 :         CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", NULL);
     462             145 :     if (pszConsiderEPSGAsURN != NULL)
     463               0 :         m_bConsiderEPSGAsURN = CSLTestBoolean(pszConsiderEPSGAsURN);
     464             145 :     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             140 :         m_bConsiderEPSGAsURN = FALSE;
     472                 : 
     473             145 :     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             145 :     int bUseExpatParserPreferably = bExpatCompatibleEncoding;
     479                 : 
     480                 :     /* Override default choice */
     481             145 :     const char* pszGMLParser = CPLGetConfigOption("GML_PARSER", NULL);
     482             145 :     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             145 :                                 m_bGetSecondaryGeometryOption );
     494             145 :     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             145 :     poReader->SetSourceFile( pszFilename );
     506                 : 
     507                 : /* -------------------------------------------------------------------- */
     508                 : /*      Find <gml:boundedBy>                                            */
     509                 : /* -------------------------------------------------------------------- */
     510                 : 
     511             145 :     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             145 :     char *pszXlinkResolvedFilename = NULL;
     519             145 :     const char *pszOption = CPLGetConfigOption("GML_SAVE_RESOLVED_TO", NULL);
     520             145 :     int bResolve = TRUE;
     521             145 :     int bHugeFile = FALSE;
     522             145 :     if( pszOption != NULL && EQUALN( pszOption, "SAME", 4 ) )
     523                 :     {
     524                 :         // "SAME" will overwrite the existing gml file
     525               0 :         pszXlinkResolvedFilename = CPLStrdup( pszFilename );
     526                 :     }
     527             145 :     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             143 :                             CPLResetExtension( pszFilename, "resolved.gml" ) );
     540                 : 
     541                 :         // Check if the file already exists.
     542                 :         VSIStatBufL sResStatBuf, sGMLStatBuf;
     543             143 :         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             145 :                                                     "ALL");
     563             145 :     char **papszSkip = NULL;
     564             145 :     if( EQUAL( pszSkipOption, "ALL" ) )
     565             138 :         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             145 :     int         bHaveSchema = FALSE;
     573             145 :     int         bSchemaDone = FALSE;
     574                 :  
     575                 : /* -------------------------------------------------------------------- */
     576                 : /*      Is some GML Feature Schema (.gfs) TEMPLATE required ?           */
     577                 : /* -------------------------------------------------------------------- */
     578                 :     const char *pszGFSTemplateName = 
     579             145 :                 CPLGetConfigOption( "GML_GFS_TEMPLATE", NULL);
     580             145 :     if( pszGFSTemplateName != NULL )
     581                 :     {
     582                 :         /* attempting to load the GFS TEMPLATE */
     583               0 :         bHaveSchema = poReader->LoadClasses( pszGFSTemplateName );
     584                 :     } 
     585                 : 
     586             145 :     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             145 :     CPLFree( pszXlinkResolvedFilename );
     612             145 :     pszXlinkResolvedFilename = NULL;
     613             145 :     CSLDestroy( papszSkip );
     614             145 :     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             145 :     if (strcmp(poReader->GetSourceFileName(), pszFilename) == 0)
     619             135 :         poReader->SetFP(fp);
     620                 :     else
     621              10 :         VSIFCloseL(fp);
     622             145 :     fp = NULL;
     623                 : 
     624                 :     /* Is a prescan required ? */
     625             145 :     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             145 :     CPLString osGFSFilename = CPLResetExtension( pszFilename, "gfs" );
     637             145 :     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             145 :     if( !bHaveSchema && osXSDFilename.size() == 0)
     644                 :     {
     645                 :         VSIStatBufL sGFSStatBuf;
     646             108 :         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             145 :     int bHasFoundXSD = FALSE;
     682                 : 
     683             145 :     if( !bHaveSchema )
     684                 :     {
     685             126 :         char** papszTypeNames = NULL;
     686                 : 
     687                 :         VSIStatBufL sXSDStatBuf;
     688             126 :         if (osXSDFilename.size() == 0)
     689                 :         {
     690              89 :             osXSDFilename = CPLResetExtension( pszFilename, "xsd" );
     691              89 :             if( bCheckAuxFile && VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
     692                 :             {
     693              61 :                 bHasFoundXSD = TRUE;
     694                 :             }
     695                 :         }
     696                 :         else
     697                 :         {
     698              37 :             if ( VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
     699                 :             {
     700              37 :                 bHasFoundXSD = TRUE;
     701                 :             }
     702                 :         }
     703                 : 
     704                 :         /* For WFS, try to fetch the application schema */
     705             264 :         if( bIsWFS && pszSchemaLocation != NULL && 
     706              92 :             (pszSchemaLocation[0] == '\'' || pszSchemaLocation[0] == '"') &&
     707              46 :              strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) != NULL )
     708                 :         {
     709              46 :             char* pszSchemaLocationTmp1 = CPLStrdup(pszSchemaLocation + 1);
     710              46 :             int nTruncLen = (int)(strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) - (pszSchemaLocation + 1));
     711              46 :             pszSchemaLocationTmp1[nTruncLen] = '\0';
     712                 :             char* pszSchemaLocationTmp2 = CPLUnescapeString(
     713              46 :                 pszSchemaLocationTmp1, NULL, CPLES_XML);
     714              46 :             CPLString osEscaped = ReplaceSpaceByPct20IfNeeded(pszSchemaLocationTmp2);
     715              46 :             CPLFree(pszSchemaLocationTmp2);
     716              46 :             pszSchemaLocationTmp2 = CPLStrdup(osEscaped);
     717              46 :             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              46 :                 char** papszTokens = CSLTokenizeString2(pszSchemaLocationTmp2, " \r\n", 0);
     724              46 :                 int nTokens = CSLCount(papszTokens);
     725              46 :                 if ((nTokens % 2) == 0)
     726                 :                 {
     727             136 :                     for(int i=0;i<nTokens;i+=2)
     728                 :                     {
     729              90 :                         char* pszLocation = CPLUnescapeString(papszTokens[i+1], NULL, CPLES_URL);
     730              90 :                         CPLString osLocation = pszLocation;
     731              90 :                         CPLFree(pszLocation);
     732              90 :                         if (osLocation.ifind("typename=") != std::string::npos &&
     733                 :                             osLocation.ifind("request=DescribeFeatureType") != std::string::npos)
     734                 :                         {
     735              41 :                             CPLString osTypeName = CPLURLGetValue(osLocation, "typename");
     736              41 :                             papszTypeNames = CSLTokenizeString2( osTypeName, ",", 0);
     737                 : 
     738              41 :                             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              41 :                             }
     759                 :                         }
     760                 :                     }
     761                 :                 }
     762              46 :                 CSLDestroy(papszTokens);
     763                 :             }
     764              46 :             CPLFree(pszSchemaLocationTmp2);
     765              46 :             CPLFree(pszSchemaLocationTmp1);
     766                 :         }
     767                 : 
     768             126 :         if( bHasFoundXSD )
     769                 :         {
     770              98 :             std::vector<GMLFeatureClass*> aosClasses;
     771              98 :             bHaveSchema = GMLParseXSD( osXSDFilename, aosClasses );
     772              98 :             if (bHaveSchema)
     773                 :             {
     774              98 :                 CPLDebug("GML", "Using %s", osXSDFilename.c_str());
     775                 : 
     776              98 :                 std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
     777              98 :                 std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
     778             317 :                 while (iter != eiter)
     779                 :                 {
     780             121 :                     GMLFeatureClass* poClass = *iter;
     781             121 :                     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             121 :                     if (bHas3D)
     787                 :                     {
     788                 :                         poClass->SetGeometryType(
     789               2 :                             poClass->GetGeometryType() | wkb25DBit);
     790                 :                     }
     791                 : 
     792             121 :                     int bAddClass = TRUE;
     793                 :                     /* If typenames are declared, only register the matching classes, in case */
     794                 :                     /* the XSD contains more layers */
     795             121 :                     if (papszTypeNames != NULL)
     796                 :                     {
     797              58 :                         bAddClass = FALSE;
     798              58 :                         char** papszIter = papszTypeNames;
     799             174 :                         while (*papszIter && !bAddClass)
     800                 :                         {
     801              58 :                             const char* pszTypeName = *papszIter;
     802              58 :                             if (strcmp(pszTypeName, poClass->GetName()) == 0)
     803               2 :                                 bAddClass = TRUE;
     804              58 :                             papszIter ++;
     805                 :                         }
     806                 : 
     807                 :                         /* Retry by removing prefixes */
     808              58 :                         if (!bAddClass)
     809                 :                         {
     810              56 :                             papszIter = papszTypeNames;
     811             168 :                             while (*papszIter && !bAddClass)
     812                 :                             {
     813              56 :                                 const char* pszTypeName = *papszIter;
     814              56 :                                 const char* pszColon = strchr(pszTypeName, ':');
     815              56 :                                 if (pszColon)
     816                 :                                 {
     817              56 :                                     pszTypeName = pszColon + 1;
     818              56 :                                     if (strcmp(pszTypeName, poClass->GetName()) == 0)
     819                 :                                     {
     820              39 :                                         poClass->SetName(pszTypeName);
     821              39 :                                         bAddClass = TRUE;
     822                 :                                     }
     823                 :                                 }
     824              56 :                                 papszIter ++;
     825                 :                             }
     826                 :                         }
     827                 : 
     828                 :                     }
     829                 : 
     830             121 :                     if (bAddClass)
     831             104 :                         poReader->AddClass( poClass );
     832                 :                     else
     833              17 :                         delete poClass;
     834                 :                 }
     835              98 :                 poReader->SetClassListLocked( TRUE );
     836              98 :             }
     837                 :         }
     838                 : 
     839             126 :         if (bHaveSchema && bIsWFS)
     840                 :         {
     841                 :             /* For WFS, we can assume sequential layers */
     842              48 :             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              47 :             else if (poReader->GetClassCount() == 0)
     850               0 :                 bHaveSchema = FALSE;
     851                 :         }
     852                 : 
     853             126 :         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             145 :     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             142 :     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             142 :     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             142 :         CPLCalloc( sizeof(OGRGMLLayer *), poReader->GetClassCount());
     917             142 :     nLayers = 0;
     918                 : 
     919             142 :     if (poReader->GetClassCount() == 1 && nNumberOfFeatures != 0)
     920                 :     {
     921              36 :         GMLFeatureClass *poClass = poReader->GetClass(0);
     922              36 :         int nFeatureCount = poClass->GetFeatureCount();
     923              36 :         if (nFeatureCount < 0)
     924                 :         {
     925              34 :             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             454 :     while( nLayers < poReader->GetClassCount() )
     934                 :     {
     935             170 :         papoLayers[nLayers] = TranslateGMLSchema(poReader->GetClass(nLayers));
     936             170 :         nLayers++;
     937                 :     }
     938                 :     
     939                 : 
     940                 :     
     941             142 :     return TRUE;
     942                 : }
     943                 : 
     944                 : /************************************************************************/
     945                 : /*                         TranslateGMLSchema()                         */
     946                 : /************************************************************************/
     947                 : 
     948             170 : OGRGMLLayer *OGRGMLDataSource::TranslateGMLSchema( GMLFeatureClass *poClass )
     949                 : 
     950                 : {
     951                 :     OGRGMLLayer *poLayer;
     952                 :     OGRwkbGeometryType eGType 
     953             170 :         = (OGRwkbGeometryType) poClass->GetGeometryType();
     954                 : 
     955             170 :     if( poClass->GetFeatureCount() == 0 )
     956               0 :         eGType = wkbUnknown;
     957                 :     
     958                 : /* -------------------------------------------------------------------- */
     959                 : /*      Create an empty layer.                                          */
     960                 : /* -------------------------------------------------------------------- */
     961                 : 
     962             170 :     const char* pszSRSName = poClass->GetSRSName();
     963             170 :     OGRSpatialReference* poSRS = NULL;
     964             170 :     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             151 :     else if (bIsWFS && poReader->GetClassCount() == 1)
     974                 :     {
     975              51 :         pszSRSName = GetGlobalSRSName();
     976              51 :         if (pszSRSName)
     977                 :         {
     978              35 :             poSRS = new OGRSpatialReference();
     979              35 :             if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
     980                 :             {
     981               0 :                 delete poSRS;
     982               0 :                 poSRS = NULL;
     983                 :             }
     984                 : 
     985              35 :             if (poSRS != NULL && m_bInvertAxisOrderIfLatLong &&
     986                 :                 GML_IsSRSLatLongOrder(pszSRSName))
     987                 :             {
     988               5 :                 OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode( "GEOGCS" );
     989               5 :                 if( poGEOGCS != NULL )
     990                 :                 {
     991               5 :                     poGEOGCS->StripNodes( "AXIS" );
     992                 : 
     993               5 :                     if (!poClass->HasExtents() &&
     994                 :                         sBoundingRect.IsInit())
     995                 :                     {
     996                 :                         poClass->SetExtents(sBoundingRect.MinY,
     997                 :                                             sBoundingRect.MaxY,
     998                 :                                             sBoundingRect.MinX,
     999               5 :                                             sBoundingRect.MaxX);
    1000                 :                     }
    1001                 :                 }
    1002                 :             }
    1003                 :         }
    1004                 : 
    1005              51 :         if (!poClass->HasExtents() &&
    1006                 :             sBoundingRect.IsInit())
    1007                 :         {
    1008                 :             poClass->SetExtents(sBoundingRect.MinX,
    1009                 :                                 sBoundingRect.MaxX,
    1010                 :                                 sBoundingRect.MinY,
    1011              30 :                                 sBoundingRect.MaxY);
    1012                 :         }
    1013                 :     }
    1014                 : 
    1015                 :     poLayer = new OGRGMLLayer( poClass->GetName(), poSRS, FALSE,
    1016             170 :                                eGType, this );
    1017             224 :     delete poSRS;
    1018                 : 
    1019                 : /* -------------------------------------------------------------------- */
    1020                 : /*      Added attributes (properties).                                  */
    1021                 : /* -------------------------------------------------------------------- */
    1022             170 :     if (bExposeGMLId)
    1023                 :     {
    1024             127 :         OGRFieldDefn oField( "gml_id", OFTString );
    1025             127 :         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
    1026                 :     }
    1027              43 :     else if (bExposeFid)
    1028                 :     {
    1029              31 :         OGRFieldDefn oField( "fid", OFTString );
    1030              31 :         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
    1031                 :     }
    1032                 : 
    1033             692 :     for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
    1034                 :     {
    1035             522 :         GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
    1036                 :         OGRFieldType eFType;
    1037                 : 
    1038             522 :         if( poProperty->GetType() == GMLPT_Untyped )
    1039               0 :             eFType = OFTString;
    1040             522 :         else if( poProperty->GetType() == GMLPT_String )
    1041             197 :             eFType = OFTString;
    1042             325 :         else if( poProperty->GetType() == GMLPT_Integer )
    1043             209 :             eFType = OFTInteger;
    1044             116 :         else if( poProperty->GetType() == GMLPT_Real )
    1045             104 :             eFType = OFTReal;
    1046              12 :         else if( poProperty->GetType() == GMLPT_StringList )
    1047               6 :             eFType = OFTStringList;
    1048               6 :         else if( poProperty->GetType() == GMLPT_IntegerList )
    1049               3 :             eFType = OFTIntegerList;
    1050               3 :         else if( poProperty->GetType() == GMLPT_RealList )
    1051               3 :             eFType = OFTRealList;
    1052                 :         else
    1053               0 :             eFType = OFTString;
    1054                 :         
    1055             522 :         OGRFieldDefn oField( poProperty->GetName(), eFType );
    1056             522 :         if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
    1057               0 :           oField.SetName(poProperty->GetName()+4);
    1058             522 :         if( poProperty->GetWidth() > 0 )
    1059             125 :             oField.SetWidth( poProperty->GetWidth() );
    1060             522 :         if( poProperty->GetPrecision() > 0 )
    1061               8 :             oField.SetPrecision( poProperty->GetPrecision() );
    1062                 : 
    1063             522 :         poLayer->GetLayerDefn()->AddFieldDefn( &oField );
    1064                 :     }
    1065                 : 
    1066             170 :     return poLayer;
    1067                 : }
    1068                 : 
    1069                 : /************************************************************************/
    1070                 : /*                         GetGlobalSRSName()                           */
    1071                 : /************************************************************************/
    1072                 : 
    1073            1199 : const char *OGRGMLDataSource::GetGlobalSRSName()
    1074                 : {
    1075            1199 :     if (poReader->CanUseGlobalSRSName() || bIsWFS)
    1076            1072 :         return poReader->GetGlobalSRSName();
    1077                 :     else
    1078             127 :         return NULL;
    1079                 : }
    1080                 : 
    1081                 : /************************************************************************/
    1082                 : /*                               Create()                               */
    1083                 : /************************************************************************/
    1084                 : 
    1085              50 : int OGRGMLDataSource::Create( const char *pszFilename, 
    1086                 :                               char **papszOptions )
    1087                 : 
    1088                 : {
    1089              50 :     if( fpOutput != NULL || poReader != NULL )
    1090                 :     {
    1091               0 :         CPLAssert( FALSE );
    1092               0 :         return FALSE;
    1093                 :     }
    1094                 : 
    1095              50 :     if( strcmp(pszFilename,"/dev/stdout") == 0 )
    1096               0 :         pszFilename = "/vsistdout/";
    1097                 : 
    1098                 : /* -------------------------------------------------------------------- */
    1099                 : /*      Read options                                                    */
    1100                 : /* -------------------------------------------------------------------- */
    1101                 : 
    1102              50 :     CSLDestroy(papszCreateOptions);
    1103              50 :     papszCreateOptions = CSLDuplicate(papszOptions);
    1104                 : 
    1105              50 :     const char* pszFormat = CSLFetchNameValue(papszCreateOptions, "FORMAT");
    1106              50 :     bIsOutputGML3 = pszFormat && EQUAL(pszFormat, "GML3");
    1107              50 :     bIsOutputGML3Deegree = pszFormat && EQUAL(pszFormat, "GML3Deegree");
    1108              50 :     bIsOutputGML32 = pszFormat && EQUAL(pszFormat, "GML3.2");
    1109              50 :     if (bIsOutputGML3Deegree || bIsOutputGML32)
    1110              18 :         bIsOutputGML3 = TRUE;
    1111                 : 
    1112                 :     bIsLongSRSRequired =
    1113              50 :         CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "GML3_LONGSRS", "YES"));
    1114                 : 
    1115                 :     bWriteSpaceIndentation =
    1116              50 :         CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "SPACE_INDENTATION", "YES"));
    1117                 : 
    1118                 : /* -------------------------------------------------------------------- */
    1119                 : /*      Create the output file.                                         */
    1120                 : /* -------------------------------------------------------------------- */
    1121              50 :     pszName = CPLStrdup( pszFilename );
    1122              50 :     osFilename = pszName;
    1123                 : 
    1124             100 :     if( strcmp(pszFilename,"/vsistdout/") == 0 ||
    1125                 :         strncmp(pszFilename,"/vsigzip/", 9) == 0 )
    1126                 :     {
    1127               0 :         fpOutput = VSIFOpenL(pszFilename, "wb");
    1128               0 :         bFpOutputIsNonSeekable = TRUE;
    1129               0 :         bFpOutputSingleFile = TRUE;
    1130                 :     }
    1131              50 :     else if ( strncmp(pszFilename,"/vsizip/", 8) == 0)
    1132                 :     {
    1133               0 :         if (EQUAL(CPLGetExtension(pszFilename), "zip"))
    1134                 :         {
    1135               0 :             CPLFree(pszName);
    1136               0 :             pszName = CPLStrdup(CPLFormFilename(pszFilename, "out.gml", NULL));
    1137                 :         }
    1138                 : 
    1139               0 :         fpOutput = VSIFOpenL(pszName, "wb");
    1140               0 :         bFpOutputIsNonSeekable = TRUE;
    1141                 :     }
    1142                 :     else
    1143              50 :         fpOutput = VSIFOpenL( pszFilename, "wb+" );
    1144              50 :     if( fpOutput == NULL )
    1145                 :     {
    1146                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
    1147                 :                   "Failed to create GML file %s.", 
    1148               0 :                   pszFilename );
    1149               0 :         return FALSE;
    1150                 :     }
    1151                 : 
    1152                 : /* -------------------------------------------------------------------- */
    1153                 : /*      Write out "standard" header.                                    */
    1154                 : /* -------------------------------------------------------------------- */
    1155                 :     PrintLine( fpOutput, "%s", 
    1156              50 :                 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" );
    1157                 : 
    1158              50 :     if (!bFpOutputIsNonSeekable)
    1159              50 :         nSchemaInsertLocation = (int) VSIFTellL( fpOutput );
    1160                 : 
    1161              50 :     const char* pszPrefix = GetAppPrefix();
    1162              50 :     const char* pszTargetNameSpace = CSLFetchNameValueDef(papszOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
    1163                 : 
    1164              50 :     PrintLine( fpOutput, "<%s:FeatureCollection", pszPrefix );
    1165                 : 
    1166              50 :     if (IsGML32Output())
    1167                 :         PrintLine( fpOutput, "%s",
    1168               9 :                 "     gml:id=\"aFeatureCollection\"" );
    1169                 : 
    1170                 : /* -------------------------------------------------------------------- */
    1171                 : /*      Write out schema info if provided in creation options.          */
    1172                 : /* -------------------------------------------------------------------- */
    1173              50 :     const char *pszSchemaURI = CSLFetchNameValue(papszOptions,"XSISCHEMAURI");
    1174              50 :     const char *pszSchemaOpt = CSLFetchNameValue( papszOptions, "XSISCHEMA" );
    1175                 : 
    1176              50 :     if( pszSchemaURI != NULL )
    1177                 :     {
    1178                 :         PrintLine( fpOutput, 
    1179               0 :               "     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
    1180                 :         PrintLine( fpOutput, 
    1181                 :               "     xsi:schemaLocation=\"%s\"", 
    1182               0 :                     pszSchemaURI );
    1183                 :     }
    1184              50 :     else if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
    1185                 :     {
    1186              50 :         char *pszBasename = CPLStrdup(CPLGetBasename( pszName ));
    1187                 : 
    1188                 :         PrintLine( fpOutput, 
    1189              50 :               "     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
    1190                 :         PrintLine( fpOutput, 
    1191                 :               "     xsi:schemaLocation=\"%s %s\"",
    1192                 :                     pszTargetNameSpace,
    1193              50 :                     CPLResetExtension( pszBasename, "xsd" ) );
    1194              50 :         CPLFree( pszBasename );
    1195                 :     }
    1196                 : 
    1197                 :     PrintLine( fpOutput,
    1198              50 :                 "     xmlns:%s=\"%s\"", pszPrefix, pszTargetNameSpace );
    1199              50 :     if (IsGML32Output())
    1200                 :         PrintLine( fpOutput, "%s",
    1201               9 :                 "     xmlns:gml=\"http://www.opengis.net/gml/3.2\">" );
    1202                 :     else
    1203                 :         PrintLine( fpOutput, "%s",
    1204              41 :                     "     xmlns:gml=\"http://www.opengis.net/gml\">" );
    1205                 : 
    1206                 : /* -------------------------------------------------------------------- */
    1207                 : /*      Should we initialize an area to place the boundedBy element?    */
    1208                 : /*      We will need to seek back to fill it in.                        */
    1209                 : /* -------------------------------------------------------------------- */
    1210              50 :     nBoundedByLocation = -1;
    1211              50 :     if( CSLFetchBoolean( papszOptions, "BOUNDEDBY", TRUE ))
    1212                 :     {
    1213              50 :         if (!bFpOutputIsNonSeekable )
    1214                 :         {
    1215              50 :             nBoundedByLocation = (int) VSIFTellL( fpOutput );
    1216                 : 
    1217              50 :             if( nBoundedByLocation != -1 )
    1218              50 :                 PrintLine( fpOutput, "%350s", "" );
    1219                 :         }
    1220                 :         else
    1221                 :         {
    1222               0 :             if (bWriteSpaceIndentation)
    1223               0 :                 VSIFPrintfL( fpOutput, "  ");
    1224               0 :             if (IsGML3Output())
    1225               0 :                 PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
    1226                 :             else
    1227               0 :                 PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
    1228                 :         }
    1229                 :     }
    1230                 : 
    1231              50 :     return TRUE;
    1232                 : }
    1233                 : 
    1234                 : /************************************************************************/
    1235                 : /*                            CreateLayer()                             */
    1236                 : /************************************************************************/
    1237                 : 
    1238                 : OGRLayer *
    1239              50 : OGRGMLDataSource::CreateLayer( const char * pszLayerName,
    1240                 :                                OGRSpatialReference *poSRS,
    1241                 :                                OGRwkbGeometryType eType,
    1242                 :                                char ** papszOptions )
    1243                 : 
    1244                 : {
    1245                 : /* -------------------------------------------------------------------- */
    1246                 : /*      Verify we are in update mode.                                   */
    1247                 : /* -------------------------------------------------------------------- */
    1248              50 :     if( fpOutput == NULL )
    1249                 :     {
    1250                 :         CPLError( CE_Failure, CPLE_NoWriteAccess,
    1251                 :                   "Data source %s opened for read access.\n"
    1252                 :                   "New layer %s cannot be created.\n",
    1253               0 :                   pszName, pszLayerName );
    1254                 : 
    1255               0 :         return NULL;
    1256                 :     }
    1257                 : 
    1258                 : /* -------------------------------------------------------------------- */
    1259                 : /*      Ensure name is safe as an element name.                         */
    1260                 : /* -------------------------------------------------------------------- */
    1261              50 :     char *pszCleanLayerName = CPLStrdup( pszLayerName );
    1262                 : 
    1263              50 :     CPLCleanXMLElementName( pszCleanLayerName );
    1264              50 :     if( strcmp(pszCleanLayerName,pszLayerName) != 0 )
    1265                 :     {
    1266                 :         CPLError( CE_Warning, CPLE_AppDefined, 
    1267                 :                   "Layer name '%s' adjusted to '%s' for XML validity.",
    1268               0 :                   pszLayerName, pszCleanLayerName );
    1269                 :     }
    1270                 : 
    1271                 : /* -------------------------------------------------------------------- */
    1272                 : /*      Set or check validity of global SRS.                            */
    1273                 : /* -------------------------------------------------------------------- */
    1274              50 :     if (nLayers == 0)
    1275                 :     {
    1276              50 :         if (poSRS)
    1277              36 :             poGlobalSRS = poSRS->Clone();
    1278                 :     }
    1279                 :     else
    1280                 :     {
    1281               0 :         if (poSRS == NULL ||
    1282                 :             (poGlobalSRS != NULL && poSRS->IsSame(poGlobalSRS)))
    1283                 :         {
    1284               0 :             delete poGlobalSRS;
    1285               0 :             poGlobalSRS = NULL;
    1286                 :         }
    1287                 :     }
    1288                 : 
    1289                 : /* -------------------------------------------------------------------- */
    1290                 : /*      Create the layer object.                                        */
    1291                 : /* -------------------------------------------------------------------- */
    1292                 :     OGRGMLLayer *poLayer;
    1293                 : 
    1294              50 :     poLayer = new OGRGMLLayer( pszCleanLayerName, poSRS, TRUE, eType, this );
    1295                 : 
    1296              50 :     CPLFree( pszCleanLayerName );
    1297                 : 
    1298                 : /* -------------------------------------------------------------------- */
    1299                 : /*      Add layer to data source layer list.                            */
    1300                 : /* -------------------------------------------------------------------- */
    1301                 :     papoLayers = (OGRGMLLayer **)
    1302              50 :         CPLRealloc( papoLayers,  sizeof(OGRGMLLayer *) * (nLayers+1) );
    1303                 :     
    1304              50 :     papoLayers[nLayers++] = poLayer;
    1305                 : 
    1306              50 :     return poLayer;
    1307                 : }
    1308                 : 
    1309                 : /************************************************************************/
    1310                 : /*                           TestCapability()                           */
    1311                 : /************************************************************************/
    1312                 : 
    1313              12 : int OGRGMLDataSource::TestCapability( const char * pszCap )
    1314                 : 
    1315                 : {
    1316              12 :     if( EQUAL(pszCap,ODsCCreateLayer) )
    1317              11 :         return TRUE;
    1318                 :     else
    1319               1 :         return FALSE;
    1320                 : }
    1321                 : 
    1322                 : /************************************************************************/
    1323                 : /*                              GetLayer()                              */
    1324                 : /************************************************************************/
    1325                 : 
    1326             230 : OGRLayer *OGRGMLDataSource::GetLayer( int iLayer )
    1327                 : 
    1328                 : {
    1329             230 :     if( iLayer < 0 || iLayer >= nLayers )
    1330               2 :         return NULL;
    1331                 :     else
    1332             228 :         return papoLayers[iLayer];
    1333                 : }
    1334                 : 
    1335                 : /************************************************************************/
    1336                 : /*                            GrowExtents()                             */
    1337                 : /************************************************************************/
    1338                 : 
    1339              70 : void OGRGMLDataSource::GrowExtents( OGREnvelope3D *psGeomBounds, int nCoordDimension )
    1340                 : 
    1341                 : {
    1342              70 :     sBoundingRect.Merge( *psGeomBounds );
    1343              70 :     if (nCoordDimension == 3)
    1344              20 :         bBBOX3D = TRUE;
    1345              70 : }
    1346                 : 
    1347                 : /************************************************************************/
    1348                 : /*                            InsertHeader()                            */
    1349                 : /*                                                                      */
    1350                 : /*      This method is used to update boundedby info for a              */
    1351                 : /*      dataset, and insert schema descriptions depending on            */
    1352                 : /*      selection options in effect.                                    */
    1353                 : /************************************************************************/
    1354                 : 
    1355              50 : void OGRGMLDataSource::InsertHeader()
    1356                 : 
    1357                 : {
    1358                 :     VSILFILE        *fpSchema;
    1359              50 :     int         nSchemaStart = 0;
    1360                 : 
    1361              50 :     if( bFpOutputSingleFile )
    1362               0 :         return;
    1363                 : 
    1364                 : /* -------------------------------------------------------------------- */
    1365                 : /*      Do we want to write the schema within the GML instance doc      */
    1366                 : /*      or to a separate file?  For now we only support external.       */
    1367                 : /* -------------------------------------------------------------------- */
    1368                 :     const char *pszSchemaURI = CSLFetchNameValue(papszCreateOptions,
    1369              50 :                                                  "XSISCHEMAURI");
    1370                 :     const char *pszSchemaOpt = CSLFetchNameValue( papszCreateOptions, 
    1371              50 :                                                   "XSISCHEMA" );
    1372                 : 
    1373              50 :     if( pszSchemaURI != NULL )
    1374               0 :         return;
    1375                 : 
    1376             100 :     if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
    1377                 :     {
    1378              50 :         const char *pszXSDFilename = CPLResetExtension( pszName, "xsd" );
    1379                 : 
    1380              50 :         fpSchema = VSIFOpenL( pszXSDFilename, "wt" );
    1381              50 :         if( fpSchema == NULL )
    1382                 :         {
    1383                 :             CPLError( CE_Failure, CPLE_OpenFailed, 
    1384                 :                       "Failed to open file %.500s for schema output.", 
    1385               0 :                       pszXSDFilename );
    1386               0 :             return;
    1387                 :         }
    1388              50 :         PrintLine( fpSchema, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );
    1389                 :     }
    1390               0 :     else if( EQUAL(pszSchemaOpt,"INTERNAL") )
    1391                 :     {
    1392               0 :         if (fpOutput == NULL)
    1393               0 :             return;
    1394               0 :         nSchemaStart = (int) VSIFTellL( fpOutput );
    1395               0 :         fpSchema = fpOutput;
    1396                 :     }
    1397                 :     else                                                               
    1398               0 :         return;
    1399                 : 
    1400                 : /* ==================================================================== */
    1401                 : /*      Write the schema section at the end of the file.  Once          */
    1402                 : /*      complete, we will read it back in, and then move the whole      */
    1403                 : /*      file "down" enough to insert the schema at the beginning.       */
    1404                 : /* ==================================================================== */
    1405                 : 
    1406                 : /* -------------------------------------------------------------------- */
    1407                 : /*      Emit the start of the schema section.                           */
    1408                 : /* -------------------------------------------------------------------- */
    1409              50 :     const char* pszPrefix = GetAppPrefix();
    1410              50 :     const char* pszTargetNameSpace = CSLFetchNameValueDef(papszCreateOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
    1411                 : 
    1412              50 :     if (IsGML3Output())
    1413                 :     {
    1414                 :         PrintLine( fpSchema,
    1415              28 :                     "<xs:schema ");
    1416                 :         PrintLine( fpSchema,
    1417              28 :                    "    targetNamespace=\"%s\"", pszTargetNameSpace );
    1418                 :         PrintLine( fpSchema,
    1419                 :                    "    xmlns:%s=\"%s\"",
    1420              28 :                     pszPrefix, pszTargetNameSpace );
    1421                 :         PrintLine( fpSchema,
    1422              28 :                    "    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"");
    1423              28 :         if (IsGML32Output())
    1424                 :         {
    1425                 :             PrintLine( fpSchema,
    1426               9 :                    "    xmlns:gml=\"http://www.opengis.net/gml/3.2\"");
    1427                 :             PrintLine( fpSchema,
    1428               9 :                     "    xmlns:gmlsf=\"http://www.opengis.net/gmlsf/2.0\"");
    1429                 :         }
    1430                 :         else
    1431                 :         {
    1432                 :             PrintLine( fpSchema,
    1433              19 :                     "    xmlns:gml=\"http://www.opengis.net/gml\"");
    1434              19 :             if (!IsGML3DeegreeOutput())
    1435                 :             {
    1436                 :                 PrintLine( fpSchema,
    1437              10 :                         "    xmlns:gmlsf=\"http://www.opengis.net/gmlsf\"");
    1438                 :             }
    1439                 :         }
    1440                 :         PrintLine( fpSchema,
    1441              28 :                    "    elementFormDefault=\"qualified\"");
    1442                 :         PrintLine( fpSchema,
    1443              28 :                    "    version=\"1.0\">");
    1444                 : 
    1445              28 :         if (IsGML32Output())
    1446                 :         {
    1447                 :             PrintLine( fpSchema,
    1448               9 :                     "<xs:annotation>");
    1449                 :             PrintLine( fpSchema,
    1450               9 :                     "  <xs:appinfo source=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\">");
    1451                 :             PrintLine( fpSchema,
    1452               9 :                     "    <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
    1453                 :             PrintLine( fpSchema,
    1454               9 :                     "  </xs:appinfo>");
    1455                 :             PrintLine( fpSchema,
    1456               9 :                     "</xs:annotation>");
    1457                 : 
    1458                 :             PrintLine( fpSchema,
    1459               9 :                         "<xs:import namespace=\"http://www.opengis.net/gml/3.2\" schemaLocation=\"http://schemas.opengis.net/gml/3.2.1/gml.xsd\"/>" );
    1460                 :             PrintLine( fpSchema,
    1461               9 :                         "<xs:import namespace=\"http://www.opengis.net/gmlsf/2.0\" schemaLocation=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\"/>" );
    1462                 :         }
    1463                 :         else
    1464                 :         {
    1465              19 :             if (!IsGML3DeegreeOutput())
    1466                 :             {
    1467                 :                 PrintLine( fpSchema,
    1468              10 :                         "<xs:annotation>");
    1469                 :                 PrintLine( fpSchema,
    1470              10 :                         "  <xs:appinfo source=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\">");
    1471                 :                 PrintLine( fpSchema,
    1472              10 :                         "    <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
    1473                 :                 PrintLine( fpSchema,
    1474              10 :                         "    <gmlsf:GMLProfileSchema>http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd</gmlsf:GMLProfileSchema>");
    1475                 :                 PrintLine( fpSchema,
    1476              10 :                         "  </xs:appinfo>");
    1477                 :                 PrintLine( fpSchema,
    1478              10 :                         "</xs:annotation>");
    1479                 :             }
    1480                 : 
    1481                 :             PrintLine( fpSchema,
    1482              19 :                         "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/>" );
    1483              19 :             if (!IsGML3DeegreeOutput())
    1484                 :             {
    1485                 :                 PrintLine( fpSchema,
    1486              10 :                             "<xs:import namespace=\"http://www.opengis.net/gmlsf\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\"/>" );
    1487                 :             }
    1488                 :         }
    1489                 :     }
    1490                 :     else
    1491                 :     {
    1492                 :         PrintLine( fpSchema,
    1493                 :                     "<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\">",
    1494              22 :                     pszTargetNameSpace, pszPrefix, pszTargetNameSpace );
    1495                 : 
    1496                 :         PrintLine( fpSchema,
    1497              22 :                     "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/>" );
    1498                 :     }
    1499                 : 
    1500                 : /* -------------------------------------------------------------------- */
    1501                 : /*      Define the FeatureCollection                                    */
    1502                 : /* -------------------------------------------------------------------- */
    1503              50 :     if (IsGML3Output())
    1504                 :     {
    1505              28 :         if (IsGML32Output())
    1506                 :         {
    1507                 :             PrintLine( fpSchema,
    1508                 :                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:AbstractGML\"/>",
    1509               9 :                         pszPrefix );
    1510                 :         }
    1511              19 :         else if (IsGML3DeegreeOutput())
    1512                 :         {
    1513                 :             PrintLine( fpSchema,
    1514                 :                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
    1515               9 :                         pszPrefix );
    1516                 :         }
    1517                 :         else
    1518                 :         {
    1519                 :             PrintLine( fpSchema,
    1520                 :                         "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_GML\"/>",
    1521              10 :                         pszPrefix );
    1522                 :         }
    1523                 : 
    1524              28 :         PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
    1525              28 :         PrintLine( fpSchema, "  <xs:complexContent>" );
    1526              28 :         if (IsGML3DeegreeOutput())
    1527                 :         {
    1528               9 :             PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
    1529               9 :             PrintLine( fpSchema, "      <xs:sequence>" );
    1530               9 :             PrintLine( fpSchema, "        <xs:element name=\"featureMember\" minOccurs=\"0\" maxOccurs=\"unbounded\">" );
    1531                 :         }
    1532                 :         else
    1533                 :         {
    1534              19 :             PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureType\">" );
    1535              19 :             PrintLine( fpSchema, "      <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" );
    1536              19 :             PrintLine( fpSchema, "        <xs:element name=\"featureMember\">" );
    1537                 :         }
    1538              28 :         PrintLine( fpSchema, "          <xs:complexType>" );
    1539              28 :         if (IsGML32Output())
    1540                 :         {
    1541               9 :             PrintLine( fpSchema, "            <xs:complexContent>" );
    1542               9 :             PrintLine( fpSchema, "              <xs:extension base=\"gml:AbstractFeatureMemberType\">" );
    1543               9 :             PrintLine( fpSchema, "                <xs:sequence>" );
    1544               9 :             PrintLine( fpSchema, "                  <xs:element ref=\"gml:AbstractFeature\"/>" );
    1545               9 :             PrintLine( fpSchema, "                </xs:sequence>" );
    1546               9 :             PrintLine( fpSchema, "              </xs:extension>" );
    1547               9 :             PrintLine( fpSchema, "            </xs:complexContent>" );
    1548                 :         }
    1549                 :         else
    1550                 :         {
    1551              19 :             PrintLine( fpSchema, "            <xs:sequence>" );
    1552              19 :             PrintLine( fpSchema, "              <xs:element ref=\"gml:_Feature\"/>" );
    1553              19 :             PrintLine( fpSchema, "            </xs:sequence>" );
    1554                 :         }
    1555              28 :         PrintLine( fpSchema, "          </xs:complexType>" );
    1556              28 :         PrintLine( fpSchema, "        </xs:element>" );
    1557              28 :         PrintLine( fpSchema, "      </xs:sequence>" );
    1558              28 :         PrintLine( fpSchema, "    </xs:extension>" );
    1559              28 :         PrintLine( fpSchema, "  </xs:complexContent>" );
    1560              28 :         PrintLine( fpSchema, "</xs:complexType>" );
    1561                 :     }
    1562                 :     else
    1563                 :     {
    1564                 :         PrintLine( fpSchema,
    1565                 :                     "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
    1566              22 :                     pszPrefix );
    1567                 : 
    1568              22 :         PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
    1569              22 :         PrintLine( fpSchema, "  <xs:complexContent>" );
    1570              22 :         PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
    1571              22 :         PrintLine( fpSchema, "      <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>" );
    1572              22 :         PrintLine( fpSchema, "      <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>" );
    1573              22 :         PrintLine( fpSchema, "    </xs:extension>" );
    1574              22 :         PrintLine( fpSchema, "  </xs:complexContent>" );
    1575              22 :         PrintLine( fpSchema, "</xs:complexType>" );
    1576                 :     }
    1577                 : 
    1578                 : /* ==================================================================== */
    1579                 : /*      Define the schema for each layer.                               */
    1580                 : /* ==================================================================== */
    1581                 :     int iLayer;
    1582                 : 
    1583             100 :     for( iLayer = 0; iLayer < GetLayerCount(); iLayer++ )
    1584                 :     {
    1585              50 :         OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn();
    1586                 :         
    1587                 : /* -------------------------------------------------------------------- */
    1588                 : /*      Emit initial stuff for a feature type.                          */
    1589                 : /* -------------------------------------------------------------------- */
    1590              50 :         if (IsGML32Output())
    1591                 :         {
    1592                 :             PrintLine(
    1593                 :                 fpSchema,
    1594                 :                 "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:AbstractFeature\"/>",
    1595               9 :                 poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
    1596                 :         }
    1597                 :         else
    1598                 :         {
    1599                 :             PrintLine(
    1600                 :                 fpSchema,
    1601                 :                 "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:_Feature\"/>",
    1602              41 :                 poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
    1603                 :         }
    1604                 : 
    1605              50 :         PrintLine( fpSchema, "<xs:complexType name=\"%s_Type\">", poFDefn->GetName());
    1606              50 :         PrintLine( fpSchema, "  <xs:complexContent>");
    1607              50 :         PrintLine( fpSchema, "    <xs:extension base=\"gml:AbstractFeatureType\">");
    1608              50 :         PrintLine( fpSchema, "      <xs:sequence>");
    1609                 : 
    1610                 : /* -------------------------------------------------------------------- */
    1611                 : /*      Define the geometry attribute.                                  */
    1612                 : /* -------------------------------------------------------------------- */
    1613              50 :         const char* pszGeometryTypeName = "GeometryPropertyType";
    1614              50 :         switch(wkbFlatten(poFDefn->GetGeomType()))
    1615                 :         {
    1616                 :             case wkbPoint:
    1617              10 :                 pszGeometryTypeName = "PointPropertyType";
    1618              10 :                 break;
    1619                 :             case wkbLineString:
    1620               4 :                 if (IsGML3Output())
    1621               3 :                     pszGeometryTypeName = "CurvePropertyType";
    1622                 :                 else
    1623               1 :                     pszGeometryTypeName = "LineStringPropertyType";
    1624               4 :                 break;
    1625                 :             case wkbPolygon:
    1626               6 :                 if (IsGML3Output())
    1627               4 :                     pszGeometryTypeName = "SurfacePropertyType";
    1628                 :                 else
    1629               2 :                     pszGeometryTypeName = "PolygonPropertyType";
    1630               6 :                 break;
    1631                 :             case wkbMultiPoint:
    1632               4 :                 pszGeometryTypeName = "MultiPointPropertyType";
    1633               4 :                 break;
    1634                 :             case wkbMultiLineString:
    1635               5 :                 if (IsGML3Output())
    1636               3 :                     pszGeometryTypeName = "MultiCurvePropertyType";
    1637                 :                 else
    1638               2 :                     pszGeometryTypeName = "MultiLineStringPropertyType";
    1639               5 :                 break;
    1640                 :             case wkbMultiPolygon:
    1641               5 :                 if (IsGML3Output())
    1642               3 :                     pszGeometryTypeName = "MultiSurfacePropertyType";
    1643                 :                 else
    1644               2 :                     pszGeometryTypeName = "MultiPolygonPropertyType";
    1645               5 :                 break;
    1646                 :             case wkbGeometryCollection:
    1647               4 :                 pszGeometryTypeName = "MultiGeometryPropertyType";
    1648                 :                 break;
    1649                 :             default:
    1650                 :                 break;
    1651                 :         }
    1652                 : 
    1653              50 :         if (poFDefn->GetGeomType() != wkbNone)
    1654                 :         {
    1655                 :             PrintLine( fpSchema,
    1656              45 :                 "        <xs:element name=\"geometryProperty\" type=\"gml:%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\"/>", pszGeometryTypeName );
    1657                 :         }
    1658                 :             
    1659                 : /* -------------------------------------------------------------------- */
    1660                 : /*      Emit each of the attributes.                                    */
    1661                 : /* -------------------------------------------------------------------- */
    1662             174 :         for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
    1663                 :         {
    1664             124 :             OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
    1665                 : 
    1666             124 :             if( poFieldDefn->GetType() == OFTInteger )
    1667                 :             {
    1668                 :                 int nWidth;
    1669                 : 
    1670              35 :                 if( poFieldDefn->GetWidth() > 0 )
    1671               2 :                     nWidth = poFieldDefn->GetWidth();
    1672                 :                 else
    1673              33 :                     nWidth = 16;
    1674                 : 
    1675              35 :                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
    1676              35 :                 PrintLine( fpSchema, "          <xs:simpleType>");
    1677              35 :                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:integer\">");
    1678              35 :                 PrintLine( fpSchema, "              <xs:totalDigits value=\"%d\"/>", nWidth);
    1679              35 :                 PrintLine( fpSchema, "            </xs:restriction>");
    1680              35 :                 PrintLine( fpSchema, "          </xs:simpleType>");
    1681              35 :                 PrintLine( fpSchema, "        </xs:element>");
    1682                 :             }
    1683              89 :             else if( poFieldDefn->GetType() == OFTReal )
    1684                 :             {
    1685                 :                 int nWidth, nDecimals;
    1686                 : 
    1687              46 :                 nWidth = poFieldDefn->GetWidth();
    1688              46 :                 nDecimals = poFieldDefn->GetPrecision();
    1689                 : 
    1690              46 :                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">",  poFieldDefn->GetNameRef());
    1691              46 :                 PrintLine( fpSchema, "          <xs:simpleType>");
    1692              46 :                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:decimal\">");
    1693              46 :                 if (nWidth > 0)
    1694                 :                 {
    1695              12 :                     PrintLine( fpSchema, "              <xs:totalDigits value=\"%d\"/>", nWidth);
    1696              12 :                     PrintLine( fpSchema, "              <xs:fractionDigits value=\"%d\"/>", nDecimals);
    1697                 :                 }
    1698              46 :                 PrintLine( fpSchema, "            </xs:restriction>");
    1699              46 :                 PrintLine( fpSchema, "          </xs:simpleType>");
    1700              46 :                 PrintLine( fpSchema, "        </xs:element>");
    1701                 :             }
    1702              43 :             else if( poFieldDefn->GetType() == OFTString )
    1703                 :             {
    1704              42 :                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">",  poFieldDefn->GetNameRef());
    1705              42 :                 PrintLine( fpSchema, "          <xs:simpleType>");
    1706              42 :                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:string\">");
    1707              42 :                 if( poFieldDefn->GetWidth() != 0 )
    1708                 :                 {
    1709               4 :                     PrintLine( fpSchema, "              <xs:maxLength value=\"%d\"/>", poFieldDefn->GetWidth());
    1710                 :                 }
    1711              42 :                 PrintLine( fpSchema, "            </xs:restriction>");
    1712              42 :                 PrintLine( fpSchema, "          </xs:simpleType>");
    1713              42 :                 PrintLine( fpSchema, "        </xs:element>");
    1714                 :             }
    1715               1 :             else if( poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime )
    1716                 :             {
    1717               1 :                 PrintLine( fpSchema, "        <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">",  poFieldDefn->GetNameRef());
    1718               1 :                 PrintLine( fpSchema, "          <xs:simpleType>");
    1719               1 :                 PrintLine( fpSchema, "            <xs:restriction base=\"xs:string\">");
    1720               1 :                 PrintLine( fpSchema, "            </xs:restriction>");
    1721               1 :                 PrintLine( fpSchema, "          </xs:simpleType>");
    1722               1 :                 PrintLine( fpSchema, "        </xs:element>");
    1723                 :             }
    1724                 :             else
    1725                 :             {
    1726                 :                 /* TODO */
    1727                 :             }
    1728                 :         } /* next field */
    1729                 : 
    1730                 : /* -------------------------------------------------------------------- */
    1731                 : /*      Finish off feature type.                                        */
    1732                 : /* -------------------------------------------------------------------- */
    1733              50 :         PrintLine( fpSchema, "      </xs:sequence>");
    1734              50 :         PrintLine( fpSchema, "    </xs:extension>");
    1735              50 :         PrintLine( fpSchema, "  </xs:complexContent>");
    1736              50 :         PrintLine( fpSchema, "</xs:complexType>" );
    1737                 :     } /* next layer */
    1738                 : 
    1739              50 :     PrintLine( fpSchema, "</xs:schema>" );
    1740                 : 
    1741                 : /* ==================================================================== */
    1742                 : /*      Move schema to the start of the file.                           */
    1743                 : /* ==================================================================== */
    1744              50 :     if( fpSchema == fpOutput )
    1745                 :     {
    1746                 : /* -------------------------------------------------------------------- */
    1747                 : /*      Read the schema into memory.                                    */
    1748                 : /* -------------------------------------------------------------------- */
    1749               0 :         int nSchemaSize = (int) VSIFTellL( fpOutput ) - nSchemaStart;
    1750               0 :         char *pszSchema = (char *) CPLMalloc(nSchemaSize+1);
    1751                 :     
    1752               0 :         VSIFSeekL( fpOutput, nSchemaStart, SEEK_SET );
    1753                 : 
    1754               0 :         VSIFReadL( pszSchema, 1, nSchemaSize, fpOutput );
    1755               0 :         pszSchema[nSchemaSize] = '\0';
    1756                 :     
    1757                 : /* -------------------------------------------------------------------- */
    1758                 : /*      Move file data down by "schema size" bytes from after <?xml>    */
    1759                 : /*      header so we have room insert the schema.  Move in pretty       */
    1760                 : /*      big chunks.                                                     */
    1761                 : /* -------------------------------------------------------------------- */
    1762               0 :         int nChunkSize = MIN(nSchemaStart-nSchemaInsertLocation,250000);
    1763               0 :         char *pszChunk = (char *) CPLMalloc(nChunkSize);
    1764               0 :         int nEndOfUnmovedData = nSchemaStart;
    1765                 : 
    1766               0 :         for( nEndOfUnmovedData = nSchemaStart;
    1767                 :              nEndOfUnmovedData > nSchemaInsertLocation; )
    1768                 :         {
    1769                 :             int nBytesToMove = 
    1770               0 :                 MIN(nChunkSize, nEndOfUnmovedData - nSchemaInsertLocation );
    1771                 : 
    1772               0 :             VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove, SEEK_SET );
    1773               0 :             VSIFReadL( pszChunk, 1, nBytesToMove, fpOutput );
    1774                 :             VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove + nSchemaSize, 
    1775               0 :                       SEEK_SET );
    1776               0 :             VSIFWriteL( pszChunk, 1, nBytesToMove, fpOutput );
    1777                 :         
    1778               0 :             nEndOfUnmovedData -= nBytesToMove;
    1779                 :         }
    1780                 : 
    1781               0 :         CPLFree( pszChunk );
    1782                 : 
    1783                 : /* -------------------------------------------------------------------- */
    1784                 : /*      Write the schema in the opened slot.                            */
    1785                 : /* -------------------------------------------------------------------- */
    1786               0 :         VSIFSeekL( fpOutput, nSchemaInsertLocation, SEEK_SET );
    1787               0 :         VSIFWriteL( pszSchema, 1, nSchemaSize, fpOutput );
    1788                 : 
    1789               0 :         VSIFSeekL( fpOutput, 0, SEEK_END );
    1790                 : 
    1791               0 :         nBoundedByLocation += nSchemaSize;
    1792                 : 
    1793               0 :         CPLFree(pszSchema);
    1794                 :     }
    1795                 : /* -------------------------------------------------------------------- */
    1796                 : /*      Close external schema files.                                    */
    1797                 : /* -------------------------------------------------------------------- */
    1798                 :     else
    1799              50 :         VSIFCloseL( fpSchema );
    1800                 : }
    1801                 : 
    1802                 : 
    1803                 : /************************************************************************/
    1804                 : /*                            PrintLine()                               */
    1805                 : /************************************************************************/
    1806                 : 
    1807            3664 : void OGRGMLDataSource::PrintLine(VSILFILE* fp, const char *fmt, ...)
    1808                 : {
    1809            3664 :     CPLString osWork;
    1810                 :     va_list args;
    1811                 : 
    1812            3664 :     va_start( args, fmt );
    1813            3664 :     osWork.vPrintf( fmt, args );
    1814            3664 :     va_end( args );
    1815                 : 
    1816                 : #ifdef WIN32
    1817                 :     const char* pszEOL = "\r\n";
    1818                 : #else
    1819            3664 :     const char* pszEOL = "\n";
    1820                 : #endif
    1821                 : 
    1822            3664 :     VSIFPrintfL(fp, "%s%s", osWork.c_str(), pszEOL);
    1823            3664 : }
    1824                 : 
    1825                 : 
    1826                 : /************************************************************************/
    1827                 : /*                     OGRGMLSingleFeatureLayer                         */
    1828                 : /************************************************************************/
    1829                 : 
    1830                 : class OGRGMLSingleFeatureLayer : public OGRLayer
    1831                 : {
    1832                 :   private:
    1833                 :     int                 nVal;
    1834                 :     OGRFeatureDefn     *poFeatureDefn;
    1835                 :     int                 iNextShapeId;
    1836                 : 
    1837                 :   public:
    1838                 :                         OGRGMLSingleFeatureLayer(int nVal );
    1839              38 :                         ~OGRGMLSingleFeatureLayer() { poFeatureDefn->Release(); }
    1840                 : 
    1841               0 :     virtual void        ResetReading() { iNextShapeId = 0; }
    1842                 :     virtual OGRFeature *GetNextFeature();
    1843               0 :     virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; }
    1844               0 :     virtual int         TestCapability( const char * ) { return FALSE; }
    1845                 : };
    1846                 : 
    1847                 : /************************************************************************/
    1848                 : /*                      OGRGMLSingleFeatureLayer()                      */
    1849                 : /************************************************************************/
    1850                 : 
    1851              38 : OGRGMLSingleFeatureLayer::OGRGMLSingleFeatureLayer( int nVal )
    1852                 : {
    1853              38 :     poFeatureDefn = new OGRFeatureDefn( "SELECT" );
    1854              38 :     poFeatureDefn->Reference();
    1855              38 :     OGRFieldDefn oField( "Validates", OFTInteger );
    1856              38 :     poFeatureDefn->AddFieldDefn( &oField );
    1857                 : 
    1858              38 :     this->nVal = nVal;
    1859              38 :     iNextShapeId = 0;
    1860              38 : }
    1861                 : 
    1862                 : /************************************************************************/
    1863                 : /*                           GetNextFeature()                           */
    1864                 : /************************************************************************/
    1865                 : 
    1866              38 : OGRFeature * OGRGMLSingleFeatureLayer::GetNextFeature()
    1867                 : {
    1868              38 :     if (iNextShapeId != 0)
    1869               0 :         return NULL;
    1870                 : 
    1871              38 :     OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
    1872              38 :     poFeature->SetField(0, nVal);
    1873              38 :     poFeature->SetFID(iNextShapeId ++);
    1874              38 :     return poFeature;
    1875                 : }
    1876                 : 
    1877                 : /************************************************************************/
    1878                 : /*                            ExecuteSQL()                              */
    1879                 : /************************************************************************/
    1880                 : 
    1881              41 : OGRLayer * OGRGMLDataSource::ExecuteSQL( const char *pszSQLCommand,
    1882                 :                                          OGRGeometry *poSpatialFilter,
    1883                 :                                          const char *pszDialect )
    1884                 : {
    1885              41 :     if (poReader != NULL && EQUAL(pszSQLCommand, "SELECT ValidateSchema()"))
    1886                 :     {
    1887              38 :         int bIsValid = FALSE;
    1888              38 :         if (osXSDFilename.size())
    1889                 :         {
    1890              38 :             CPLErrorReset();
    1891              38 :             bIsValid = CPLValidateXML(osFilename, osXSDFilename, NULL);
    1892                 :         }
    1893              38 :         return new OGRGMLSingleFeatureLayer(bIsValid);
    1894                 :     }
    1895                 : 
    1896               3 :     return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
    1897                 : }
    1898                 : 
    1899                 : /************************************************************************/
    1900                 : /*                          ReleaseResultSet()                          */
    1901                 : /************************************************************************/
    1902                 : 
    1903              40 : void OGRGMLDataSource::ReleaseResultSet( OGRLayer * poResultsSet )
    1904                 : {
    1905              40 :     delete poResultsSet;
    1906              40 : }
    1907                 : 
    1908                 : /************************************************************************/
    1909                 : /*                         FindAndParseBoundedBy()                      */
    1910                 : /************************************************************************/
    1911                 : 
    1912             145 : void OGRGMLDataSource::FindAndParseBoundedBy(VSILFILE* fp)
    1913                 : {
    1914                 :     /* Build a shortened XML file that contain only the global */
    1915                 :     /* boundedBy element, so as to be able to parse it easily */
    1916                 : 
    1917                 :     char szStartTag[128];
    1918             145 :     char* pszXML = (char*)CPLMalloc(8192 + 128 + 3 + 1);
    1919             145 :     VSIFSeekL(fp, 0, SEEK_SET);
    1920             145 :     int nRead = (int)VSIFReadL(pszXML, 1, 8192, fp);
    1921             145 :     pszXML[nRead] = 0;
    1922                 : 
    1923             145 :     const char* pszStartTag = strchr(pszXML, '<');
    1924             145 :     if (pszStartTag != NULL)
    1925                 :     {
    1926             428 :         while (pszStartTag != NULL && pszStartTag[1] == '?')
    1927             138 :             pszStartTag = strchr(pszStartTag + 1, '<');
    1928                 : 
    1929             145 :         if (pszStartTag != NULL)
    1930                 :         {
    1931             145 :             pszStartTag ++;
    1932             145 :             const char* pszEndTag = strchr(pszStartTag, ' ');
    1933             290 :             if (pszEndTag != NULL && pszEndTag - pszStartTag < 128 )
    1934                 :             {
    1935             145 :                 memcpy(szStartTag, pszStartTag, pszEndTag - pszStartTag);
    1936             145 :                 szStartTag[pszEndTag - pszStartTag] = '\0';
    1937                 :             }
    1938                 :             else
    1939               0 :                 pszStartTag = NULL;
    1940                 :         }
    1941                 :     }
    1942                 : 
    1943             145 :     char* pszEndBoundedBy = strstr(pszXML, "</wfs:boundedBy>");
    1944             145 :     int bWFSBoundedBy = FALSE;
    1945             145 :     if (pszEndBoundedBy != NULL)
    1946               2 :         bWFSBoundedBy = TRUE;
    1947                 :     else
    1948             143 :         pszEndBoundedBy = strstr(pszXML, "</gml:boundedBy>");
    1949             145 :     if (pszStartTag != NULL && pszEndBoundedBy != NULL)
    1950                 :     {
    1951             121 :         const char* pszSRSName = NULL;
    1952                 :         char szSRSName[128];
    1953                 : 
    1954             121 :         szSRSName[0] = '\0';
    1955                 : 
    1956                 :         /* Find a srsName somewhere for some WFS 2.0 documents */
    1957                 :         /* that have not it set at the <wfs:boundedBy> element */
    1958                 :         /* e.g. http://geoserv.weichand.de:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAME=bvv:gmd_ex */
    1959             121 :         if( bIsWFS )
    1960                 :         {
    1961              48 :             pszSRSName = strstr(pszXML, "srsName=\"");
    1962              48 :             if( pszSRSName != NULL )
    1963                 :             {
    1964              47 :                 pszSRSName += 9;
    1965              47 :                 const char* pszEndQuote = strchr(pszSRSName, '"');
    1966              47 :                 if (pszEndQuote != NULL &&
    1967                 :                     (int)(pszEndQuote - pszSRSName) < sizeof(szSRSName))
    1968                 :                 {
    1969              47 :                     memcpy(szSRSName, pszSRSName, pszEndQuote - pszSRSName);
    1970              47 :                     szSRSName[pszEndQuote - pszSRSName] = '\0';
    1971                 :                 }
    1972              47 :                 pszSRSName = NULL;
    1973                 :             }
    1974                 :         }
    1975                 : 
    1976             121 :         pszEndBoundedBy[strlen("</gml:boundedBy>")] = '\0';
    1977             121 :         strcat(pszXML, "</");
    1978             121 :         strcat(pszXML, szStartTag);
    1979             121 :         strcat(pszXML, ">");
    1980                 : 
    1981             121 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    1982             121 :         CPLXMLNode* psXML = CPLParseXMLString(pszXML);
    1983             121 :         CPLPopErrorHandler();
    1984             121 :         CPLErrorReset();
    1985             121 :         if (psXML != NULL)
    1986                 :         {
    1987             120 :             CPLXMLNode* psBoundedBy = NULL;
    1988             120 :             CPLXMLNode* psIter = psXML;
    1989             361 :             while(psIter != NULL)
    1990                 :             {
    1991                 :                 psBoundedBy = CPLGetXMLNode(psIter, bWFSBoundedBy ?
    1992             241 :                                         "wfs:boundedBy" : "gml:boundedBy");
    1993             241 :                 if (psBoundedBy != NULL)
    1994             120 :                     break;
    1995             121 :                 psIter = psIter->psNext;
    1996                 :             }
    1997                 : 
    1998             120 :             const char* pszLowerCorner = NULL;
    1999             120 :             const char* pszUpperCorner = NULL;
    2000             120 :             if (psBoundedBy != NULL)
    2001                 :             {
    2002             120 :                 CPLXMLNode* psEnvelope = CPLGetXMLNode(psBoundedBy, "gml:Envelope");
    2003             120 :                 if (psEnvelope)
    2004                 :                 {
    2005              80 :                     pszSRSName = CPLGetXMLValue(psEnvelope, "srsName", NULL);
    2006              80 :                     pszLowerCorner = CPLGetXMLValue(psEnvelope, "gml:lowerCorner", NULL);
    2007              80 :                     pszUpperCorner = CPLGetXMLValue(psEnvelope, "gml:upperCorner", NULL);
    2008                 :                 }
    2009                 :             }
    2010                 : 
    2011             122 :             if( bIsWFS && pszSRSName == NULL &&
    2012                 :                 pszLowerCorner != NULL && pszUpperCorner != NULL &&
    2013               2 :                 szSRSName[0] != '\0' )
    2014                 :             {
    2015               2 :                 pszSRSName = szSRSName;
    2016                 :             }
    2017                 : 
    2018             120 :             if (pszSRSName != NULL && pszLowerCorner != NULL && pszUpperCorner != NULL)
    2019                 :             {
    2020              79 :                 char** papszLC = CSLTokenizeString(pszLowerCorner);
    2021              79 :                 char** papszUC = CSLTokenizeString(pszUpperCorner);
    2022              79 :                 if (CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2)
    2023                 :                 {
    2024              79 :                     CPLDebug("GML", "Global SRS = %s", pszSRSName);
    2025                 : 
    2026              79 :                     if (strncmp(pszSRSName, "http://www.opengis.net/gml/srs/epsg.xml#", 40) == 0)
    2027                 :                     {
    2028               0 :                         std::string osWork;
    2029               0 :                         osWork.assign("EPSG:", 5);
    2030               0 :                         osWork.append(pszSRSName+40);
    2031               0 :                         poReader->SetGlobalSRSName(osWork.c_str());
    2032                 :                     }
    2033                 :                     else
    2034              79 :                         poReader->SetGlobalSRSName(pszSRSName);
    2035                 : 
    2036              79 :                     double dfMinX = CPLAtofM(papszLC[0]);
    2037              79 :                     double dfMinY = CPLAtofM(papszLC[1]);
    2038              79 :                     double dfMaxX = CPLAtofM(papszUC[0]);
    2039              79 :                     double dfMaxY = CPLAtofM(papszUC[1]);
    2040                 : 
    2041              79 :                     SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY);
    2042                 :                 }
    2043              79 :                 CSLDestroy(papszLC);
    2044              79 :                 CSLDestroy(papszUC);
    2045                 :             }
    2046                 : 
    2047             120 :             CPLDestroyXMLNode(psXML);
    2048                 :         }
    2049                 :     }
    2050                 : 
    2051             145 :     CPLFree(pszXML);
    2052             145 : }
    2053                 : 
    2054                 : /************************************************************************/
    2055                 : /*                             SetExtents()                             */
    2056                 : /************************************************************************/
    2057                 : 
    2058              79 : void OGRGMLDataSource::SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
    2059                 : {
    2060              79 :     sBoundingRect.MinX = dfMinX;
    2061              79 :     sBoundingRect.MinY = dfMinY;
    2062              79 :     sBoundingRect.MaxX = dfMaxX;
    2063              79 :     sBoundingRect.MaxY = dfMaxY;
    2064              79 : }
    2065                 : 
    2066                 : /************************************************************************/
    2067                 : /*                             GetAppPrefix()                           */
    2068                 : /************************************************************************/
    2069                 : 
    2070             235 : const char* OGRGMLDataSource::GetAppPrefix()
    2071                 : {
    2072             235 :     return CSLFetchNameValueDef(papszCreateOptions, "PREFIX", "ogr");
    2073                 : }

Generated by: LCOV version 1.7