LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/gmt - ogrgmtlayer.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 430 330 76.7 %
Date: 2011-12-18 Functions: 18 12 66.7 %

       1                 : /******************************************************************************
       2                 :  * $Id: ogrgmtlayer.cpp 10645 2007-01-18 02:22:39Z warmerdam $
       3                 :  *
       4                 :  * Project:  OpenGIS Simple Features Reference Implementation
       5                 :  * Purpose:  Implements OGRGmtLayer class.
       6                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2007, 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                 : #include "ogr_gmt.h"
      31                 : #include "cpl_conv.h"
      32                 : #include "ogr_p.h"
      33                 : 
      34                 : CPL_CVSID("$Id: ogrgmtlayer.cpp 10645 2007-01-18 02:22:39Z warmerdam $");
      35                 : 
      36                 : /************************************************************************/
      37                 : /*                            OGRGmtLayer()                             */
      38                 : /************************************************************************/
      39                 : 
      40               5 : OGRGmtLayer::OGRGmtLayer( const char * pszFilename, int bUpdate )
      41                 : 
      42                 : {
      43               5 :     poSRS = NULL;
      44                 :     
      45               5 :     iNextFID = 0;
      46               5 :     bValidFile = FALSE;
      47               5 :     bHeaderComplete = !bUpdate; // assume header complete in readonly mode.
      48               5 :     eWkbType = wkbUnknown;
      49               5 :     poFeatureDefn = NULL;
      50               5 :     papszKeyedValues = NULL;
      51                 : 
      52               5 :     this->bUpdate = bUpdate;
      53                 : 
      54               5 :     bRegionComplete = FALSE;
      55               5 :     nRegionOffset = 0;
      56                 : 
      57                 : /* -------------------------------------------------------------------- */
      58                 : /*      Open file.                                                      */
      59                 : /* -------------------------------------------------------------------- */
      60               5 :     if( bUpdate )
      61               2 :         fp = VSIFOpenL( pszFilename, "r+" );
      62                 :     else
      63               3 :         fp = VSIFOpenL( pszFilename, "r" );
      64                 :     
      65               5 :     if( fp == NULL )
      66               0 :         return;
      67                 : 
      68                 : /* -------------------------------------------------------------------- */
      69                 : /*      Read the header.                                                */
      70                 : /* -------------------------------------------------------------------- */
      71               5 :     CPLString osFieldNames, osFieldTypes, osGeometryType, osRegion;
      72               5 :     CPLString osWKT, osProj4, osEPSG;
      73               5 :     vsi_l_offset nStartOfLine = VSIFTellL(fp);
      74                 :     
      75              31 :     while( ReadLine() && osLine[0] == '#' )
      76                 :     {
      77                 :         int iKey;
      78                 : 
      79              24 :         if( strstr( osLine, "FEATURE_DATA" ) )
      80                 :         {
      81               3 :             bHeaderComplete = TRUE;
      82               3 :             ReadLine();
      83               3 :             break;
      84                 :         }
      85                 : 
      86              21 :         if( EQUALN( osLine, "# REGION_STUB ", 14 ) )
      87               2 :             nRegionOffset = nStartOfLine;
      88                 : 
      89              80 :         for( iKey = 0; 
      90              39 :              papszKeyedValues != NULL && papszKeyedValues[iKey] != NULL; 
      91                 :              iKey++ )
      92                 :         {
      93              20 :             if( papszKeyedValues[iKey][0] == 'N' )
      94               3 :                 osFieldNames = papszKeyedValues[iKey] + 1;
      95              20 :             if( papszKeyedValues[iKey][0] == 'T' )
      96               3 :                 osFieldTypes = papszKeyedValues[iKey] + 1;
      97              20 :             if( papszKeyedValues[iKey][0] == 'G' )
      98               3 :                 osGeometryType = papszKeyedValues[iKey] + 1;
      99              20 :             if( papszKeyedValues[iKey][0] == 'R' )
     100               3 :                 osRegion = papszKeyedValues[iKey] + 1;
     101              20 :             if( papszKeyedValues[iKey][0] == 'J' )
     102                 :             {
     103               3 :                 CPLString osArg = papszKeyedValues[iKey] + 2;
     104               3 :                 if( osArg[0] == '"' && osArg[osArg.length()-1] == '"' )
     105                 :                 {
     106               2 :                     osArg = osArg.substr(1,osArg.length()-2);
     107                 :                     char *pszArg = CPLUnescapeString(osArg, NULL,
     108               2 :                                                      CPLES_BackslashQuotable);
     109               2 :                     osArg = pszArg;
     110               2 :                     CPLFree( pszArg );
     111                 :                 }
     112                 :                     
     113               3 :                 if( papszKeyedValues[iKey][1] == 'e' )
     114               1 :                     osEPSG = osArg;
     115               3 :                 if( papszKeyedValues[iKey][1] == 'p' )
     116               1 :                     osProj4 = osArg;
     117               3 :                 if( papszKeyedValues[iKey][1] == 'w' )
     118               1 :                     osWKT = osArg;
     119                 :             }
     120                 :         }
     121                 : 
     122              21 :         nStartOfLine = VSIFTellL(fp);
     123                 :     }
     124                 : 
     125                 : /* -------------------------------------------------------------------- */
     126                 : /*      Handle coordinate system.                                       */
     127                 : /* -------------------------------------------------------------------- */
     128               5 :     if( osWKT.length() )
     129                 :     {
     130               1 :         char *pszWKT = (char *) osWKT.c_str();
     131                 : 
     132               1 :         poSRS = new OGRSpatialReference();
     133               2 :         if( poSRS->importFromWkt(&pszWKT) != OGRERR_NONE )
     134                 :         {
     135               0 :             delete poSRS;
     136               0 :             poSRS = NULL;
     137                 :         }
     138                 :     }
     139               4 :     else if( osEPSG.length() )
     140                 :     {
     141               0 :         poSRS = new OGRSpatialReference();
     142               0 :         if( poSRS->importFromEPSG( atoi(osEPSG) ) != OGRERR_NONE )
     143                 :         {
     144               0 :             delete poSRS;
     145               0 :             poSRS = NULL;
     146                 :         }
     147                 :     }
     148               4 :     else if( osProj4.length() )
     149                 :     {
     150               0 :         poSRS = new OGRSpatialReference();
     151               0 :         if( poSRS->importFromProj4( osProj4 ) != OGRERR_NONE )
     152                 :         {
     153               0 :             delete poSRS;
     154               0 :             poSRS = NULL;
     155                 :         }
     156                 :     }
     157                 : 
     158                 : /* -------------------------------------------------------------------- */
     159                 : /*      Create the feature definition, and set the geometry type, if    */
     160                 : /*      known.                                                          */
     161                 : /* -------------------------------------------------------------------- */
     162               5 :     poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) );
     163               5 :     poFeatureDefn->Reference();
     164                 : 
     165               5 :     if( osGeometryType == "POINT" )
     166               0 :         poFeatureDefn->SetGeomType( wkbPoint );
     167               5 :     else if( osGeometryType == "MULTIPOINT" )
     168               0 :         poFeatureDefn->SetGeomType( wkbMultiPoint );
     169               5 :     else if( osGeometryType == "LINESTRING" )
     170               0 :         poFeatureDefn->SetGeomType( wkbLineString );
     171               5 :     else if( osGeometryType == "MULTILINESTRING" )
     172               1 :         poFeatureDefn->SetGeomType( wkbMultiLineString );
     173               4 :     else if( osGeometryType == "POLYGON" )
     174               1 :         poFeatureDefn->SetGeomType( wkbPolygon );
     175               3 :     else if( osGeometryType == "MULTIPOLYGON" )
     176               1 :         poFeatureDefn->SetGeomType( wkbMultiPolygon );
     177                 : 
     178                 : /* -------------------------------------------------------------------- */
     179                 : /*      Process a region line.                                          */
     180                 : /* -------------------------------------------------------------------- */
     181               5 :     if( osRegion.length() > 0 )
     182                 :     {
     183                 :         char **papszTokens = CSLTokenizeStringComplex( osRegion.c_str(),
     184               3 :                                                        "/", FALSE, FALSE );
     185                 :         
     186               3 :         if( CSLCount(papszTokens) == 4 )
     187                 :         {
     188               3 :             sRegion.MinX = CPLAtofM(papszTokens[0]);
     189               3 :             sRegion.MaxX = CPLAtofM(papszTokens[1]);
     190               3 :             sRegion.MinY = CPLAtofM(papszTokens[2]);
     191               3 :             sRegion.MaxY = CPLAtofM(papszTokens[3]);
     192                 :         }
     193                 : 
     194               3 :         bRegionComplete = TRUE;
     195                 : 
     196               3 :         CSLDestroy( papszTokens );
     197                 :     }
     198                 : 
     199                 : /* -------------------------------------------------------------------- */
     200                 : /*      Process fields.                                                 */
     201                 : /* -------------------------------------------------------------------- */
     202               5 :     if( osFieldNames.length() || osFieldTypes.length() )
     203                 :     {
     204                 :         char **papszFN = CSLTokenizeStringComplex( osFieldNames, "|", 
     205               3 :                                                    TRUE, TRUE );
     206                 :         char **papszFT = CSLTokenizeStringComplex( osFieldTypes, "|", 
     207               3 :                                                    TRUE, TRUE );
     208               3 :         int nFieldCount = MAX(CSLCount(papszFN),CSLCount(papszFT));
     209                 :         int iField;
     210                 : 
     211              10 :         for( iField = 0; iField < nFieldCount; iField++ )
     212                 :         {
     213               7 :             OGRFieldDefn oField("", OFTString );
     214                 : 
     215               7 :             if( iField < CSLCount(papszFN) )
     216               7 :                 oField.SetName( papszFN[iField] );
     217                 :             else
     218               0 :                 oField.SetName( CPLString().Printf( "Field_%d", iField+1 ));
     219                 : 
     220               7 :             if( iField < CSLCount(papszFT) )
     221                 :             {
     222               7 :                 if( EQUAL(papszFT[iField],"integer") )
     223               3 :                     oField.SetType( OFTInteger );
     224               4 :                 else if( EQUAL(papszFT[iField],"double") )
     225               1 :                     oField.SetType( OFTReal );
     226               3 :                 else if( EQUAL(papszFT[iField],"datetime") )
     227               1 :                     oField.SetType( OFTDateTime );
     228                 :             }
     229                 : 
     230               7 :             poFeatureDefn->AddFieldDefn( &oField );
     231                 :         }
     232                 : 
     233               3 :         CSLDestroy( papszFN );
     234               3 :         CSLDestroy( papszFT );
     235                 :     }
     236                 : 
     237               5 :     bValidFile = TRUE;
     238               0 : }
     239                 : 
     240                 : /************************************************************************/
     241                 : /*                           ~OGRGmtLayer()                           */
     242                 : /************************************************************************/
     243                 : 
     244               5 : OGRGmtLayer::~OGRGmtLayer()
     245                 : 
     246                 : {
     247               5 :     if( m_nFeaturesRead > 0 && poFeatureDefn != NULL )
     248                 :     {
     249                 :         CPLDebug( "Gmt", "%d features read on layer '%s'.",
     250                 :                   (int) m_nFeaturesRead, 
     251               3 :                   poFeatureDefn->GetName() );
     252                 :     }
     253                 : 
     254                 : /* -------------------------------------------------------------------- */
     255                 : /*      Write out the region bounds if we know where they go, and we    */
     256                 : /*      are in update mode.                                             */
     257                 : /* -------------------------------------------------------------------- */
     258               5 :     if( nRegionOffset != 0 && bUpdate )
     259                 :     {
     260               2 :         VSIFSeekL( fp, nRegionOffset, SEEK_SET );
     261                 :         VSIFPrintfL( fp, "# @R%.12g/%.12g/%.12g/%.12g", 
     262                 :                      sRegion.MinX, 
     263                 :                      sRegion.MaxX,
     264                 :                      sRegion.MinY,
     265               2 :                      sRegion.MaxY );
     266                 :     }
     267                 :     
     268                 : /* -------------------------------------------------------------------- */
     269                 : /*      Clean up.                                                       */
     270                 : /* -------------------------------------------------------------------- */
     271               5 :     CSLDestroy( papszKeyedValues );
     272                 : 
     273               5 :     if( poFeatureDefn )
     274               5 :         poFeatureDefn->Release();
     275                 : 
     276               5 :     if( poSRS )
     277               1 :         poSRS->Release();
     278                 : 
     279               5 :     if( fp != NULL )
     280               5 :         VSIFCloseL( fp );
     281               5 : }
     282                 : 
     283                 : /************************************************************************/
     284                 : /*                              ReadLine()                              */
     285                 : /*                                                                      */
     286                 : /*      Read a line into osLine.  If it is a comment line with @        */
     287                 : /*      keyed values, parse out the keyed values into                   */
     288                 : /*      papszKeyedValues.                                               */
     289                 : /************************************************************************/
     290                 : 
     291             691 : int OGRGmtLayer::ReadLine()
     292                 : 
     293                 : {
     294                 : /* -------------------------------------------------------------------- */
     295                 : /*      Clear last line.                                                */
     296                 : /* -------------------------------------------------------------------- */
     297             691 :     osLine.erase();
     298             691 :     if( papszKeyedValues )
     299                 :     {
     300             114 :         CSLDestroy( papszKeyedValues );
     301             114 :         papszKeyedValues = NULL;
     302                 :     }
     303                 :     
     304                 : /* -------------------------------------------------------------------- */
     305                 : /*      Read newline.                                                   */
     306                 : /* -------------------------------------------------------------------- */
     307             691 :     const char *pszLine = CPLReadLineL( fp );
     308             691 :     if( pszLine == NULL )
     309               6 :         return FALSE; // end of file.
     310                 : 
     311             685 :     osLine = pszLine;
     312                 : 
     313                 : /* -------------------------------------------------------------------- */
     314                 : /*      If this is a comment line with keyed values, parse them.        */
     315                 : /* -------------------------------------------------------------------- */
     316                 :     size_t i;
     317                 : 
     318             805 :     if( osLine[0] != '#' || osLine.find_first_of('@') == std::string::npos )
     319             571 :         return TRUE;
     320                 : 
     321             617 :     for( i = 0; i < osLine.length(); i++ )
     322                 :     {
     323             503 :         if( osLine[i] == '@' )
     324                 :         {
     325                 :             size_t iValEnd;
     326             115 :             int bInQuotes = FALSE;
     327                 : 
     328            1738 :             for( iValEnd = i+2; iValEnd < osLine.length(); iValEnd++ )
     329                 :             {
     330            1628 :                 if( !bInQuotes && isspace((unsigned char)osLine[iValEnd]) )
     331               5 :                     break;
     332                 : 
     333            1623 :                 if( bInQuotes && osLine[iValEnd] == '\\' 
     334                 :                     && iValEnd < osLine.length()-1 )
     335                 :                 {
     336              30 :                     iValEnd++;
     337                 :                 }
     338            1593 :                 else if( osLine[iValEnd] == '"' )
     339              16 :                     bInQuotes = !bInQuotes;
     340                 :             }
     341                 : 
     342             115 :             CPLString osValue = osLine.substr(i+2,iValEnd-i-2);
     343                 : 
     344                 :             // Unecape contents
     345                 :             char *pszUEValue = CPLUnescapeString( osValue, NULL, 
     346             115 :                                                   CPLES_BackslashQuotable );
     347                 :             
     348             115 :             CPLString osKeyValue = osLine.substr(i+1,1);
     349             115 :             osKeyValue += pszUEValue;
     350             115 :             CPLFree( pszUEValue );
     351             115 :             papszKeyedValues = CSLAddString( papszKeyedValues, osKeyValue );
     352                 : 
     353             115 :             i = iValEnd;
     354                 :         }
     355                 :     }
     356                 : 
     357             114 :     return TRUE;
     358                 : }
     359                 : 
     360                 : /************************************************************************/
     361                 : /*                            ResetReading()                            */
     362                 : /************************************************************************/
     363                 : 
     364               2 : void OGRGmtLayer::ResetReading()
     365                 : 
     366                 : {
     367               2 :     if( iNextFID != 0 )
     368                 :     {
     369               1 :         iNextFID = 0;
     370               1 :         VSIFSeekL( fp, 0, SEEK_SET );
     371               1 :         ReadLine();
     372                 :     }
     373               2 : }
     374                 : 
     375                 : /************************************************************************/
     376                 : /*                          ScanAheadForHole()                          */
     377                 : /*                                                                      */
     378                 : /*      Scan ahead to see if the next geometry is a hole.  If so        */
     379                 : /*      return TRUE, otherwise seek back to where we were and return    */
     380                 : /*      FALSE.                                                          */
     381                 : /************************************************************************/
     382                 : 
     383              21 : int OGRGmtLayer::ScanAheadForHole()
     384                 : 
     385                 : {
     386              21 :     CPLString osSavedLine = osLine;
     387              21 :     vsi_l_offset nSavedLocation = VSIFTellL( fp );
     388                 : 
     389              81 :     while( ReadLine() && osLine[0] == '#' )
     390                 :     {
     391              40 :         if( papszKeyedValues != NULL && papszKeyedValues[0][0] == 'H' )
     392               1 :             return TRUE;
     393                 :     }
     394                 : 
     395              20 :     VSIFSeekL( fp, nSavedLocation, SEEK_SET );
     396              20 :     osLine = osSavedLine;
     397                 : 
     398                 :     // We don't actually restore papszKeyedValues, but we 
     399                 :     // assume it doesn't matter since this method is only called
     400                 :     // when processing the '>' line.
     401                 : 
     402              20 :     return FALSE;
     403                 : }
     404                 : 
     405                 : /************************************************************************/
     406                 : /*                           NextIsFeature()                            */
     407                 : /*                                                                      */
     408                 : /*      Returns TRUE if the next line is a feature attribute line.      */
     409                 : /*      This generally indicates the end of a multilinestring or        */
     410                 : /*      multipolygon feature.                                           */
     411                 : /************************************************************************/
     412                 : 
     413               5 : int OGRGmtLayer::NextIsFeature()
     414                 : 
     415                 : {
     416               5 :     CPLString osSavedLine = osLine;
     417               5 :     vsi_l_offset nSavedLocation = VSIFTellL( fp );
     418               5 :     int bReturn = FALSE;
     419                 : 
     420               5 :     ReadLine();
     421                 : 
     422               5 :     if( osLine[0] == '#' && strstr(osLine,"@D") != NULL )
     423               2 :         bReturn = TRUE;
     424                 : 
     425               5 :     VSIFSeekL( fp, nSavedLocation, SEEK_SET );
     426               5 :     osLine = osSavedLine;
     427                 : 
     428                 :     // We don't actually restore papszKeyedValues, but we 
     429                 :     // assume it doesn't matter since this method is only called
     430                 :     // when processing the '>' line.
     431                 : 
     432               5 :     return bReturn;
     433                 : }
     434                 : 
     435                 : /************************************************************************/
     436                 : /*                         GetNextRawFeature()                          */
     437                 : /************************************************************************/
     438                 : 
     439              27 : OGRFeature *OGRGmtLayer::GetNextRawFeature()
     440                 : 
     441                 : {
     442                 :     int  bMultiVertex = 
     443                 :         poFeatureDefn->GetGeomType() != wkbPoint
     444              27 :         && poFeatureDefn->GetGeomType() != wkbUnknown;
     445              27 :     CPLString osFieldData;
     446              27 :     OGRGeometry *poGeom = NULL;
     447                 : 
     448                 : /* -------------------------------------------------------------------- */
     449                 : /*      Read lines associated with this feature.                        */
     450                 : /* -------------------------------------------------------------------- */
     451             596 :     for( ; TRUE; ReadLine() )
     452                 :     {
     453             623 :         if( osLine.length() == 0 )
     454               7 :             break;
     455                 : 
     456             616 :         if( osLine[0] == '>' )
     457                 :         {
     458              72 :             if( poGeom != NULL 
     459              24 :                 && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon )
     460                 :             {
     461               3 :                 OGRMultiPolygon *poMP = (OGRMultiPolygon *) poGeom;
     462               3 :                 if( ScanAheadForHole() )
     463                 :                 {
     464                 :                     // Add a hole to the current polygon.
     465                 :                     ((OGRPolygon *) poMP->getGeometryRef(
     466                 :                         poMP->getNumGeometries()-1 ))->
     467               1 :                         addRingDirectly( new OGRLinearRing() );
     468                 :                 }
     469               2 :                 else if( !NextIsFeature() )
     470                 :                 {
     471               1 :                     OGRPolygon *poPoly = new OGRPolygon();
     472                 :                     
     473               2 :                     poPoly->addRingDirectly( new OGRLinearRing() );
     474                 : 
     475               1 :                     poMP->addGeometryDirectly( poPoly );
     476                 :                 }
     477                 :                 else
     478               1 :                     break; /* done geometry */
     479                 :             }
     480              66 :             else if( poGeom != NULL 
     481              21 :                      && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
     482                 :             {
     483              18 :                 if( ScanAheadForHole() )
     484                 :                     ((OGRPolygon *)poGeom)->
     485               0 :                         addRingDirectly( new OGRLinearRing() );
     486                 :                 else
     487              18 :                     break; /* done geometry */
     488                 :             }
     489              30 :             else if( poGeom != NULL 
     490               3 :                      && (wkbFlatten(poGeom->getGeometryType()) 
     491                 :                          == wkbMultiLineString)
     492                 :                      && !NextIsFeature() )
     493                 :             {
     494                 :                 ((OGRMultiLineString *) poGeom)->
     495               2 :                     addGeometryDirectly( new OGRLineString() );
     496                 :             }
     497              25 :             else if( poGeom != NULL )
     498                 :             {
     499               1 :                 break;
     500                 :             }
     501              24 :             else if( poFeatureDefn->GetGeomType() == wkbUnknown )
     502                 :             {
     503               0 :                 poFeatureDefn->SetGeomType( wkbLineString );
     504               0 :                 bMultiVertex = TRUE;
     505                 :             }
     506                 :         }
     507             568 :         else if( osLine[0] == '#' )
     508                 :         {
     509                 :             int i;
     510             209 :             for( i = 0; 
     511             104 :                  papszKeyedValues != NULL && papszKeyedValues[i] != NULL; 
     512                 :                  i++ )
     513                 :             {
     514              52 :                 if( papszKeyedValues[i][0] == 'D' )
     515              24 :                     osFieldData = papszKeyedValues[i] + 1;
     516                 :             }
     517                 :         }
     518                 :         else
     519                 :         {
     520                 :             // Parse point line. 
     521             515 :             double dfX, dfY, dfZ = 0.0;
     522             515 :             int nDim = sscanf( osLine, "%lf %lf %lf", &dfX, &dfY, &dfZ );
     523                 :                 
     524             515 :             if( nDim >= 2 )
     525                 :             {
     526             515 :                 if( poGeom == NULL )
     527                 :                 {
     528              24 :                     switch( poFeatureDefn->GetGeomType() )
     529                 :                     {
     530                 :                       case wkbLineString:
     531               0 :                         poGeom = new OGRLineString();
     532               0 :                         break;
     533                 :                     
     534                 :                       case wkbPolygon:
     535              20 :                         poGeom = new OGRPolygon();
     536                 :                         ((OGRPolygon *) poGeom)->addRingDirectly(
     537              40 :                             new OGRLinearRing() );
     538              20 :                         break;
     539                 :                     
     540                 :                       case wkbMultiPolygon:
     541                 :                       {
     542               2 :                           OGRPolygon *poPoly = new OGRPolygon();
     543               4 :                           poPoly->addRingDirectly( new OGRLinearRing() );
     544                 : 
     545               2 :                           poGeom = new OGRMultiPolygon();
     546                 :                           ((OGRMultiPolygon *) poGeom)->
     547               2 :                               addGeometryDirectly( poPoly );
     548                 :                       }
     549               2 :                       break;
     550                 :                     
     551                 :                       case wkbMultiPoint:
     552               0 :                         poGeom = new OGRMultiPoint();
     553               0 :                         break;
     554                 : 
     555                 :                       case wkbMultiLineString:
     556               2 :                         poGeom = new OGRMultiLineString();
     557                 :                         ((OGRMultiLineString *) poGeom)->addGeometryDirectly(
     558               4 :                             new OGRLineString() );
     559               2 :                         break;
     560                 : 
     561                 :                       case wkbPoint:
     562                 :                       case wkbUnknown:
     563                 :                       default:
     564               0 :                         poGeom = new OGRPoint();
     565                 :                         break;
     566                 :                     }
     567                 : 
     568                 :                 }
     569                 : 
     570             515 :                 switch( wkbFlatten(poGeom->getGeometryType()) )
     571                 :                 {
     572                 :                   case wkbPoint:
     573               0 :                     ((OGRPoint *) poGeom)->setX( dfX );
     574               0 :                     ((OGRPoint *) poGeom)->setY( dfY );
     575               0 :                     if( nDim == 3 )
     576               0 :                         ((OGRPoint *) poGeom)->setZ( dfZ );
     577               0 :                     break;
     578                 : 
     579                 :                   case wkbLineString:
     580               0 :                     if( nDim == 3 )
     581               0 :                         ((OGRLineString *)poGeom)->addPoint(dfX,dfY,dfZ);
     582                 :                     else
     583               0 :                         ((OGRLineString *)poGeom)->addPoint(dfX,dfY);
     584               0 :                     break;
     585                 : 
     586                 :                   case wkbPolygon:
     587                 :                   case wkbMultiPolygon:
     588                 :                   {
     589                 :                       OGRPolygon *poPoly;
     590                 :                       OGRLinearRing *poRing;
     591                 : 
     592             507 :                       if( wkbFlatten(poGeom->getGeometryType()) 
     593                 :                           == wkbMultiPolygon )
     594                 :                       {
     595              17 :                           OGRMultiPolygon *poMP = (OGRMultiPolygon *) poGeom;
     596                 :                           poPoly = (OGRPolygon*) poMP->getGeometryRef(
     597              17 :                               poMP->getNumGeometries() - 1 );
     598                 :                       }
     599                 :                       else
     600             490 :                           poPoly = (OGRPolygon *) poGeom;
     601                 : 
     602             507 :                       if( poPoly->getNumInteriorRings() == 0 )
     603             503 :                           poRing = poPoly->getExteriorRing();
     604                 :                       else
     605                 :                           poRing = poPoly->getInteriorRing(
     606               4 :                               poPoly->getNumInteriorRings()-1 );
     607                 :                       
     608             507 :                       if( nDim == 3 )
     609               0 :                         poRing->addPoint(dfX,dfY,dfZ);
     610                 :                       else
     611             507 :                         poRing->addPoint(dfX,dfY);
     612                 :                   }
     613             507 :                   break;
     614                 : 
     615                 :                   case wkbMultiLineString:
     616                 :                   {
     617               8 :                       OGRMultiLineString *poML = (OGRMultiLineString *) poGeom;
     618                 :                       OGRLineString *poLine;
     619                 : 
     620                 :                       poLine = (OGRLineString *) 
     621               8 :                           poML->getGeometryRef( poML->getNumGeometries()-1 );
     622                 :                       
     623               8 :                       if( nDim == 3 )
     624               0 :                         poLine->addPoint(dfX,dfY,dfZ);
     625                 :                       else
     626               8 :                         poLine->addPoint(dfX,dfY);
     627                 :                   }
     628               8 :                   break;
     629                 : 
     630                 :                   default:
     631               0 :                     CPLAssert( FALSE );
     632                 :                 }
     633                 :             }
     634                 :         }
     635                 : 
     636             596 :         if( poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint )
     637                 :         {
     638               0 :             ReadLine();
     639               0 :             break;
     640                 :         }
     641                 :     }
     642                 : 
     643              27 :     if( poGeom == NULL )
     644               3 :         return NULL;
     645                 : 
     646                 : /* -------------------------------------------------------------------- */
     647                 : /*      Create feature.                                                 */
     648                 : /* -------------------------------------------------------------------- */
     649              24 :     OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
     650              24 :     poFeature->SetGeometryDirectly( poGeom );
     651              24 :     poFeature->SetFID( iNextFID++ );
     652                 : 
     653                 : /* -------------------------------------------------------------------- */
     654                 : /*      Process field values.                                           */
     655                 : /* -------------------------------------------------------------------- */
     656              24 :     char **papszFD = CSLTokenizeStringComplex( osFieldData, "|", TRUE, TRUE );
     657                 :     int iField; 
     658                 : 
     659              92 :     for( iField = 0; papszFD != NULL && papszFD[iField] != NULL; iField++ )
     660                 :     {
     661              68 :         if( iField >= poFeatureDefn->GetFieldCount() )
     662               0 :             break;
     663                 : 
     664              68 :         poFeature->SetField( iField, papszFD[iField] );
     665                 :     }
     666                 : 
     667              24 :     CSLDestroy( papszFD );
     668                 : 
     669              24 :     m_nFeaturesRead++;
     670                 : 
     671              24 :     return poFeature;
     672                 : }
     673                 : 
     674                 : /************************************************************************/
     675                 : /*                           GetNextFeature()                           */
     676                 : /************************************************************************/
     677                 : 
     678              27 : OGRFeature *OGRGmtLayer::GetNextFeature()
     679                 : 
     680                 : {
     681               5 :     while( TRUE )
     682                 :     {
     683              27 :         OGRFeature *poFeature = GetNextRawFeature();
     684                 : 
     685              27 :         if( poFeature == NULL )
     686               3 :             return NULL;
     687                 : 
     688              24 :         if( (m_poFilterGeom == NULL
     689                 :              || FilterGeometry( poFeature->GetGeometryRef() ) )
     690                 :             && (m_poAttrQuery == NULL
     691                 :                 || m_poAttrQuery->Evaluate( poFeature ) ) )
     692                 :         {
     693              19 :             return poFeature;
     694                 :         }
     695                 :         else
     696                 :         {
     697               5 :             delete poFeature;
     698                 :         }
     699                 :     }
     700                 : 
     701                 :     return NULL;
     702                 : }
     703                 : 
     704                 : /************************************************************************/
     705                 : /*                           CompleteHeader()                           */
     706                 : /*                                                                      */
     707                 : /*      Finish writing out the header with field definitions and the    */
     708                 : /*      layer geometry type.                                            */
     709                 : /************************************************************************/
     710                 : 
     711               2 : OGRErr OGRGmtLayer::CompleteHeader( OGRGeometry *poThisGeom )
     712                 : 
     713                 : {
     714                 : /* -------------------------------------------------------------------- */
     715                 : /*      If we don't already have a geometry type, try to work one       */
     716                 : /*      out and write it now.                                           */
     717                 : /* -------------------------------------------------------------------- */
     718               2 :     if( poFeatureDefn->GetGeomType() == wkbUnknown 
     719                 :         && poThisGeom != NULL )
     720                 :     {
     721                 :         const char *pszGeom;
     722                 : 
     723               2 :         poFeatureDefn->SetGeomType(wkbFlatten(poThisGeom->getGeometryType()));
     724                 : 
     725               2 :         switch( wkbFlatten(poFeatureDefn->GetGeomType()) )
     726                 :         {
     727                 :           case wkbPoint:
     728               0 :             pszGeom = " @GPOINT";
     729               0 :             break;
     730                 :           case wkbLineString:
     731               0 :             pszGeom = " @GLINESTRING";
     732               0 :             break;
     733                 :           case wkbPolygon:
     734               1 :             pszGeom = " @GPOLYGON";
     735               1 :             break;
     736                 :           case wkbMultiPoint:
     737               0 :             pszGeom = " @GMULTIPOINT";
     738               0 :             break;
     739                 :           case wkbMultiLineString:
     740               0 :             pszGeom = " @GMULTILINESTRING";
     741               0 :             break;
     742                 :           case wkbMultiPolygon:
     743               1 :             pszGeom = " @GMULTIPOLYGON";
     744               1 :             break;
     745                 :           default:
     746               0 :             pszGeom = "";
     747                 :             break;
     748                 :         }
     749                 :         
     750               2 :         VSIFPrintfL( fp, "#%s\n", pszGeom );
     751                 :     }
     752                 : 
     753                 : /* -------------------------------------------------------------------- */
     754                 : /*      Prepare and write the field names and types.                    */
     755                 : /* -------------------------------------------------------------------- */
     756               2 :     CPLString osFieldNames, osFieldTypes;
     757                 :         
     758                 :     int iField;
     759                 : 
     760               6 :     for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
     761                 :     {
     762               4 :         if( iField > 0 )
     763                 :         {
     764               2 :             osFieldNames += "|";
     765               2 :             osFieldTypes += "|";
     766                 :         }
     767                 : 
     768               4 :         osFieldNames += poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
     769               4 :         switch( poFeatureDefn->GetFieldDefn(iField)->GetType() )
     770                 :         {
     771                 :           case OFTInteger:
     772               2 :             osFieldTypes += "integer";
     773               2 :             break;
     774                 :                 
     775                 :           case OFTReal:
     776               1 :             osFieldTypes += "double";
     777               1 :             break;
     778                 :                 
     779                 :           case OFTDateTime:
     780               0 :             osFieldTypes += "datetime";
     781               0 :             break;
     782                 :                 
     783                 :           default:
     784               1 :             osFieldTypes += "string";
     785                 :             break;
     786                 :         }
     787                 :     }
     788                 : 
     789               2 :     if( poFeatureDefn->GetFieldCount() > 0 )
     790                 :     {
     791               2 :         VSIFPrintfL( fp, "# @N%s\n", osFieldNames.c_str() );
     792               2 :         VSIFPrintfL( fp, "# @T%s\n", osFieldTypes.c_str() );
     793                 :     }
     794                 : 
     795                 : /* -------------------------------------------------------------------- */
     796                 : /*      Mark the end of the header, and start of feature data.          */
     797                 : /* -------------------------------------------------------------------- */
     798               2 :     VSIFPrintfL( fp, "# FEATURE_DATA\n" );
     799                 : 
     800               2 :     bHeaderComplete = TRUE;
     801               2 :     bRegionComplete = TRUE; // no feature written, so we know them all!
     802                 : 
     803               2 :     return OGRERR_NONE;
     804                 : }
     805                 : 
     806                 : /************************************************************************/
     807                 : /*                           CreateFeature()                            */
     808                 : /************************************************************************/
     809                 : 
     810              12 : OGRErr OGRGmtLayer::CreateFeature( OGRFeature *poFeature )
     811                 : 
     812                 : {
     813              12 :     if( !bUpdate )
     814                 :     {
     815                 :         CPLError( CE_Failure, CPLE_NoWriteAccess, 
     816               0 :                   "Can't create features on read-only dataset." );
     817               0 :         return OGRERR_FAILURE;
     818                 :     }
     819                 : 
     820                 : /* -------------------------------------------------------------------- */
     821                 : /*      Do we need to write the header describing the fields?           */
     822                 : /* -------------------------------------------------------------------- */
     823              12 :     if( !bHeaderComplete )
     824                 :     {
     825               2 :         OGRErr eErr = CompleteHeader( poFeature->GetGeometryRef() );
     826                 : 
     827               2 :         if( eErr != OGRERR_NONE )
     828               0 :             return eErr;
     829                 :     }
     830                 : 
     831                 : /* -------------------------------------------------------------------- */
     832                 : /*      Write out the feature                                           */
     833                 : /* -------------------------------------------------------------------- */
     834              12 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     835                 :     
     836              12 :     if( poGeom == NULL )
     837                 :     {
     838                 :         CPLError( CE_Failure, CPLE_AppDefined, 
     839               0 :                   "Features without geometry not supported by GMT writer." );
     840               0 :         return OGRERR_FAILURE;
     841                 :     }
     842                 : 
     843              12 :     if( poFeatureDefn->GetGeomType() == wkbUnknown )
     844               0 :         poFeatureDefn->SetGeomType(wkbFlatten(poGeom->getGeometryType()));
     845                 : 
     846                 :     // Do we need a vertex collection marker grouping vertices. 
     847              12 :     if( poFeatureDefn->GetGeomType() != wkbPoint )
     848              12 :         VSIFPrintfL( fp, ">\n" );
     849                 : 
     850                 : /* -------------------------------------------------------------------- */
     851                 : /*      Write feature properties()                                      */
     852                 : /* -------------------------------------------------------------------- */
     853              12 :     if( poFeatureDefn->GetFieldCount() > 0 )
     854                 :     {
     855                 :         int iField;
     856              12 :         CPLString osFieldData;
     857                 : 
     858              44 :         for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
     859                 :         {
     860              32 :             OGRFieldType eFType=poFeatureDefn->GetFieldDefn(iField)->GetType();
     861              32 :             const char *pszRawValue = poFeature->GetFieldAsString(iField);
     862                 :             char *pszEscapedVal;
     863                 : 
     864              32 :             if( iField > 0 )
     865              20 :                 osFieldData += "|";
     866                 : 
     867                 :             // We don't want prefix spaces for numeric values.
     868              32 :             if( eFType == OFTInteger || eFType == OFTReal )
     869              44 :                 while( *pszRawValue == ' ' )
     870               0 :                     pszRawValue++;
     871                 : 
     872              32 :             if( strchr(pszRawValue,' ') || strchr(pszRawValue,'|') 
     873                 :                 || strchr(pszRawValue, '\t') || strchr(pszRawValue, '\n') )
     874                 :             {
     875                 :                 pszEscapedVal = 
     876                 :                     CPLEscapeString( pszRawValue, 
     877               0 :                                      -1, CPLES_BackslashQuotable );
     878                 :                 
     879               0 :                 osFieldData += "\"";
     880               0 :                 osFieldData += pszEscapedVal;
     881               0 :                 osFieldData += "\"";
     882               0 :                 CPLFree( pszEscapedVal );
     883                 :             }
     884                 :             else
     885              32 :                 osFieldData += pszRawValue;
     886                 :         }
     887                 : 
     888              12 :         VSIFPrintfL( fp, "# @D%s\n", osFieldData.c_str() );
     889                 :     }
     890                 : 
     891                 : /* -------------------------------------------------------------------- */
     892                 : /*      Write Geometry                                                  */
     893                 : /* -------------------------------------------------------------------- */
     894              12 :     return WriteGeometry( (OGRGeometryH) poGeom, TRUE );
     895                 : }
     896                 : 
     897                 : /************************************************************************/
     898                 : /*                           WriteGeometry()                            */
     899                 : /*                                                                      */
     900                 : /*      Write a geometry to the file.  If bHaveAngle is TRUE it         */
     901                 : /*      means the angle bracket preceeding the point stream has         */
     902                 : /*      already been written out.                                       */
     903                 : /*                                                                      */
     904                 : /*      We use the C API for geometry access because of it's            */
     905                 : /*      simplified access to vertices and children geometries.          */
     906                 : /************************************************************************/
     907                 : 
     908              29 : OGRErr OGRGmtLayer::WriteGeometry( OGRGeometryH hGeom, int bHaveAngle )
     909                 : 
     910                 : {
     911                 : /* -------------------------------------------------------------------- */
     912                 : /*      This is a geometry with sub-geometries.                         */
     913                 : /* -------------------------------------------------------------------- */
     914              29 :     if( OGR_G_GetGeometryCount( hGeom ) > 0 )
     915                 :     {
     916                 :         int iGeom;
     917              15 :         OGRErr eErr = OGRERR_NONE;
     918                 :         
     919              32 :         for( iGeom = 0; 
     920                 :              iGeom < OGR_G_GetGeometryCount(hGeom) && eErr == OGRERR_NONE;
     921                 :              iGeom++ )
     922                 :         {
     923                 :             // We need to emit polygon @P and @H items while we still
     924                 :             // know this is a polygon and which is the outer and inner
     925                 :             // ring. 
     926              17 :             if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon )
     927                 :             {
     928              14 :                 if( !bHaveAngle )
     929                 :                 {
     930               2 :                     VSIFPrintfL( fp, ">\n" );
     931               2 :                     bHaveAngle = TRUE;
     932                 :                 }
     933              14 :                 if( iGeom == 0 )
     934              13 :                     VSIFPrintfL( fp, "# @P\n" );
     935                 :                 else
     936               1 :                     VSIFPrintfL( fp, "# @H\n" );
     937                 :             }
     938                 : 
     939                 :             eErr = WriteGeometry( OGR_G_GetGeometryRef( hGeom, iGeom ), 
     940              17 :                                   bHaveAngle );
     941              17 :             bHaveAngle = FALSE;
     942                 :         }
     943              15 :         return eErr;
     944                 :     }
     945                 : 
     946                 : /* -------------------------------------------------------------------- */
     947                 : /*      If this is not a point we need to have an angle bracket to      */
     948                 : /*      mark the vertex list.                                           */
     949                 : /* -------------------------------------------------------------------- */
     950              14 :     if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) != wkbPoint 
     951                 :         && !bHaveAngle )
     952               0 :         VSIFPrintfL( fp, ">\n" );
     953                 : 
     954                 : /* -------------------------------------------------------------------- */
     955                 : /*      Dump vertices.                                                  */
     956                 : /* -------------------------------------------------------------------- */
     957              14 :     int iPoint, nPointCount = OGR_G_GetPointCount(hGeom);
     958              14 :     int nDim = OGR_G_GetCoordinateDimension(hGeom);
     959                 : 
     960             276 :     for( iPoint = 0; iPoint < nPointCount; iPoint++ )
     961                 :     {
     962                 :         char   szLine[128];
     963             262 :         double dfX = OGR_G_GetX( hGeom, iPoint );
     964             262 :         double dfY = OGR_G_GetY( hGeom, iPoint );
     965             262 :         double dfZ = OGR_G_GetZ( hGeom, iPoint );
     966                 : 
     967             262 :         sRegion.Merge( dfX, dfY );
     968             262 :         OGRMakeWktCoordinate( szLine, dfX, dfY, dfZ, nDim );
     969             262 :         if( VSIFPrintfL( fp, "%s\n", szLine ) < 1 )
     970                 :         {
     971                 :             CPLError( CE_Failure, CPLE_FileIO, 
     972                 :                       "Gmt write failure: %s", 
     973               0 :                       VSIStrerror( errno ) );
     974               0 :             return OGRERR_FAILURE;
     975                 :         }
     976                 :     }
     977                 : 
     978              14 :     return OGRERR_NONE;
     979                 : }
     980                 : 
     981                 : /************************************************************************/
     982                 : /*                             GetExtent()                              */
     983                 : /*                                                                      */
     984                 : /*      Fetch extent of the data currently stored in the dataset.       */
     985                 : /*      The bForce flag has no effect on SHO files since that value     */
     986                 : /*      is always in the header.                                        */
     987                 : /*                                                                      */
     988                 : /*      Returns OGRERR_NONE/OGRRERR_FAILURE.                            */
     989                 : /************************************************************************/
     990                 : 
     991               0 : OGRErr OGRGmtLayer::GetExtent (OGREnvelope *psExtent, int bForce)
     992                 : 
     993                 : {
     994               0 :     if( bRegionComplete && sRegion.IsInit() )
     995                 :     {
     996               0 :         *psExtent = sRegion;
     997               0 :         return OGRERR_NONE;
     998                 :     }
     999                 : 
    1000               0 :     return OGRLayer::GetExtent( psExtent, bForce );
    1001                 : }
    1002                 : 
    1003                 : /************************************************************************/
    1004                 : /*                           TestCapability()                           */
    1005                 : /************************************************************************/
    1006                 : 
    1007               0 : int OGRGmtLayer::TestCapability( const char * pszCap )
    1008                 : 
    1009                 : {
    1010               0 :     if( EQUAL(pszCap,OLCRandomRead) )
    1011               0 :         return FALSE;
    1012                 : 
    1013               0 :     else if( EQUAL(pszCap,OLCSequentialWrite) )
    1014               0 :         return TRUE;
    1015                 : 
    1016               0 :     else if( EQUAL(pszCap,OLCFastSpatialFilter) )
    1017               0 :         return FALSE;
    1018                 : 
    1019               0 :     else if( EQUAL(pszCap,OLCFastGetExtent) )
    1020               0 :         return bRegionComplete;
    1021                 : 
    1022               0 :     else if( EQUAL(pszCap,OLCCreateField) )
    1023               0 :         return TRUE;
    1024                 : 
    1025                 :     else 
    1026               0 :         return FALSE;
    1027                 : }
    1028                 : 
    1029                 : /************************************************************************/
    1030                 : /*                            CreateField()                             */
    1031                 : /************************************************************************/
    1032                 : 
    1033               4 : OGRErr OGRGmtLayer::CreateField( OGRFieldDefn *poField, int bApproxOK )
    1034                 : 
    1035                 : {
    1036               4 :     if( !bUpdate )
    1037                 :     {
    1038                 :         CPLError( CE_Failure, CPLE_NoWriteAccess, 
    1039               0 :                   "Can't create fields on read-only dataset." );
    1040               0 :         return OGRERR_FAILURE;
    1041                 :     }
    1042                 : 
    1043               4 :     if( bHeaderComplete )
    1044                 :     {
    1045                 :         CPLError( CE_Failure, CPLE_AppDefined, 
    1046               0 :                   "Unable to create fields after features have been created.");
    1047               0 :         return OGRERR_FAILURE;
    1048                 :     }
    1049                 : 
    1050               4 :     switch( poField->GetType() )
    1051                 :     {
    1052                 :       case OFTInteger:
    1053                 :       case OFTReal:
    1054                 :       case OFTString:
    1055                 :       case OFTDateTime:
    1056               4 :         poFeatureDefn->AddFieldDefn( poField );
    1057               4 :         return OGRERR_NONE;
    1058                 :         break;
    1059                 : 
    1060                 :         break;
    1061                 : 
    1062                 :       default:
    1063               0 :         if( !bApproxOK )
    1064                 :         {
    1065                 :             CPLError( CE_Failure, CPLE_AppDefined, 
    1066                 :                       "Field %s is of unsupported type %s.", 
    1067                 :                       poField->GetNameRef(), 
    1068               0 :                       poField->GetFieldTypeName( poField->GetType() ) );
    1069               0 :             return OGRERR_FAILURE;
    1070                 :         } 
    1071               0 :         else if( poField->GetType() == OFTDate 
    1072                 :                  || poField->GetType() == OFTTime )
    1073                 :         {
    1074               0 :             OGRFieldDefn oModDef( poField );
    1075               0 :             oModDef.SetType( OFTDateTime );
    1076               0 :             poFeatureDefn->AddFieldDefn( poField );
    1077               0 :             return OGRERR_NONE;
    1078                 :         }
    1079                 :         else 
    1080                 :         {
    1081               0 :             OGRFieldDefn oModDef( poField );
    1082               0 :             oModDef.SetType( OFTString );
    1083               0 :             poFeatureDefn->AddFieldDefn( poField );
    1084               0 :             return OGRERR_NONE;
    1085                 :         }
    1086                 :     }
    1087                 : }
    1088                 : 
    1089                 : /************************************************************************/
    1090                 : /*                           GetSpatialRef()                            */
    1091                 : /************************************************************************/
    1092                 : 
    1093               0 : OGRSpatialReference *OGRGmtLayer::GetSpatialRef()
    1094                 : 
    1095                 : {
    1096               0 :     return poSRS;
    1097                 : }

Generated by: LCOV version 1.7