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: 2012-04-28 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              10 : OGRGmtLayer::OGRGmtLayer( const char * pszFilename, int bUpdate )
      41                 : 
      42                 : {
      43              10 :     poSRS = NULL;
      44                 :     
      45              10 :     iNextFID = 0;
      46              10 :     bValidFile = FALSE;
      47              10 :     bHeaderComplete = !bUpdate; // assume header complete in readonly mode.
      48              10 :     eWkbType = wkbUnknown;
      49              10 :     poFeatureDefn = NULL;
      50              10 :     papszKeyedValues = NULL;
      51                 : 
      52              10 :     this->bUpdate = bUpdate;
      53                 : 
      54              10 :     bRegionComplete = FALSE;
      55              10 :     nRegionOffset = 0;
      56                 : 
      57                 : /* -------------------------------------------------------------------- */
      58                 : /*      Open file.                                                      */
      59                 : /* -------------------------------------------------------------------- */
      60              10 :     if( bUpdate )
      61               4 :         fp = VSIFOpenL( pszFilename, "r+" );
      62                 :     else
      63               6 :         fp = VSIFOpenL( pszFilename, "r" );
      64                 :     
      65              10 :     if( fp == NULL )
      66               0 :         return;
      67                 : 
      68                 : /* -------------------------------------------------------------------- */
      69                 : /*      Read the header.                                                */
      70                 : /* -------------------------------------------------------------------- */
      71              10 :     CPLString osFieldNames, osFieldTypes, osGeometryType, osRegion;
      72              10 :     CPLString osWKT, osProj4, osEPSG;
      73              10 :     vsi_l_offset nStartOfLine = VSIFTellL(fp);
      74                 :     
      75              62 :     while( ReadLine() && osLine[0] == '#' )
      76                 :     {
      77                 :         int iKey;
      78                 : 
      79              48 :         if( strstr( osLine, "FEATURE_DATA" ) )
      80                 :         {
      81               6 :             bHeaderComplete = TRUE;
      82               6 :             ReadLine();
      83               6 :             break;
      84                 :         }
      85                 : 
      86              42 :         if( EQUALN( osLine, "# REGION_STUB ", 14 ) )
      87               4 :             nRegionOffset = nStartOfLine;
      88                 : 
      89             160 :         for( iKey = 0; 
      90              78 :              papszKeyedValues != NULL && papszKeyedValues[iKey] != NULL; 
      91                 :              iKey++ )
      92                 :         {
      93              40 :             if( papszKeyedValues[iKey][0] == 'N' )
      94               6 :                 osFieldNames = papszKeyedValues[iKey] + 1;
      95              40 :             if( papszKeyedValues[iKey][0] == 'T' )
      96               6 :                 osFieldTypes = papszKeyedValues[iKey] + 1;
      97              40 :             if( papszKeyedValues[iKey][0] == 'G' )
      98               6 :                 osGeometryType = papszKeyedValues[iKey] + 1;
      99              40 :             if( papszKeyedValues[iKey][0] == 'R' )
     100               6 :                 osRegion = papszKeyedValues[iKey] + 1;
     101              40 :             if( papszKeyedValues[iKey][0] == 'J' )
     102                 :             {
     103               6 :                 CPLString osArg = papszKeyedValues[iKey] + 2;
     104               6 :                 if( osArg[0] == '"' && osArg[osArg.length()-1] == '"' )
     105                 :                 {
     106               4 :                     osArg = osArg.substr(1,osArg.length()-2);
     107                 :                     char *pszArg = CPLUnescapeString(osArg, NULL,
     108               4 :                                                      CPLES_BackslashQuotable);
     109               4 :                     osArg = pszArg;
     110               4 :                     CPLFree( pszArg );
     111                 :                 }
     112                 :                     
     113               6 :                 if( papszKeyedValues[iKey][1] == 'e' )
     114               2 :                     osEPSG = osArg;
     115               6 :                 if( papszKeyedValues[iKey][1] == 'p' )
     116               2 :                     osProj4 = osArg;
     117               6 :                 if( papszKeyedValues[iKey][1] == 'w' )
     118               2 :                     osWKT = osArg;
     119                 :             }
     120                 :         }
     121                 : 
     122              42 :         nStartOfLine = VSIFTellL(fp);
     123                 :     }
     124                 : 
     125                 : /* -------------------------------------------------------------------- */
     126                 : /*      Handle coordinate system.                                       */
     127                 : /* -------------------------------------------------------------------- */
     128              10 :     if( osWKT.length() )
     129                 :     {
     130               2 :         char *pszWKT = (char *) osWKT.c_str();
     131                 : 
     132               2 :         poSRS = new OGRSpatialReference();
     133               4 :         if( poSRS->importFromWkt(&pszWKT) != OGRERR_NONE )
     134                 :         {
     135               0 :             delete poSRS;
     136               0 :             poSRS = NULL;
     137                 :         }
     138                 :     }
     139               8 :     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               8 :     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              10 :     poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) );
     163              10 :     poFeatureDefn->Reference();
     164                 : 
     165              10 :     if( osGeometryType == "POINT" )
     166               0 :         poFeatureDefn->SetGeomType( wkbPoint );
     167              10 :     else if( osGeometryType == "MULTIPOINT" )
     168               0 :         poFeatureDefn->SetGeomType( wkbMultiPoint );
     169              10 :     else if( osGeometryType == "LINESTRING" )
     170               0 :         poFeatureDefn->SetGeomType( wkbLineString );
     171              10 :     else if( osGeometryType == "MULTILINESTRING" )
     172               2 :         poFeatureDefn->SetGeomType( wkbMultiLineString );
     173               8 :     else if( osGeometryType == "POLYGON" )
     174               2 :         poFeatureDefn->SetGeomType( wkbPolygon );
     175               6 :     else if( osGeometryType == "MULTIPOLYGON" )
     176               2 :         poFeatureDefn->SetGeomType( wkbMultiPolygon );
     177                 : 
     178                 : /* -------------------------------------------------------------------- */
     179                 : /*      Process a region line.                                          */
     180                 : /* -------------------------------------------------------------------- */
     181              10 :     if( osRegion.length() > 0 )
     182                 :     {
     183                 :         char **papszTokens = CSLTokenizeStringComplex( osRegion.c_str(),
     184               6 :                                                        "/", FALSE, FALSE );
     185                 :         
     186               6 :         if( CSLCount(papszTokens) == 4 )
     187                 :         {
     188               6 :             sRegion.MinX = CPLAtofM(papszTokens[0]);
     189               6 :             sRegion.MaxX = CPLAtofM(papszTokens[1]);
     190               6 :             sRegion.MinY = CPLAtofM(papszTokens[2]);
     191               6 :             sRegion.MaxY = CPLAtofM(papszTokens[3]);
     192                 :         }
     193                 : 
     194               6 :         bRegionComplete = TRUE;
     195                 : 
     196               6 :         CSLDestroy( papszTokens );
     197                 :     }
     198                 : 
     199                 : /* -------------------------------------------------------------------- */
     200                 : /*      Process fields.                                                 */
     201                 : /* -------------------------------------------------------------------- */
     202              10 :     if( osFieldNames.length() || osFieldTypes.length() )
     203                 :     {
     204                 :         char **papszFN = CSLTokenizeStringComplex( osFieldNames, "|", 
     205               6 :                                                    TRUE, TRUE );
     206                 :         char **papszFT = CSLTokenizeStringComplex( osFieldTypes, "|", 
     207               6 :                                                    TRUE, TRUE );
     208               6 :         int nFieldCount = MAX(CSLCount(papszFN),CSLCount(papszFT));
     209                 :         int iField;
     210                 : 
     211              20 :         for( iField = 0; iField < nFieldCount; iField++ )
     212                 :         {
     213              14 :             OGRFieldDefn oField("", OFTString );
     214                 : 
     215              14 :             if( iField < CSLCount(papszFN) )
     216              14 :                 oField.SetName( papszFN[iField] );
     217                 :             else
     218               0 :                 oField.SetName( CPLString().Printf( "Field_%d", iField+1 ));
     219                 : 
     220              14 :             if( iField < CSLCount(papszFT) )
     221                 :             {
     222              14 :                 if( EQUAL(papszFT[iField],"integer") )
     223               6 :                     oField.SetType( OFTInteger );
     224               8 :                 else if( EQUAL(papszFT[iField],"double") )
     225               2 :                     oField.SetType( OFTReal );
     226               6 :                 else if( EQUAL(papszFT[iField],"datetime") )
     227               2 :                     oField.SetType( OFTDateTime );
     228                 :             }
     229                 : 
     230              14 :             poFeatureDefn->AddFieldDefn( &oField );
     231                 :         }
     232                 : 
     233               6 :         CSLDestroy( papszFN );
     234               6 :         CSLDestroy( papszFT );
     235                 :     }
     236                 : 
     237              10 :     bValidFile = TRUE;
     238               0 : }
     239                 : 
     240                 : /************************************************************************/
     241                 : /*                           ~OGRGmtLayer()                           */
     242                 : /************************************************************************/
     243                 : 
     244              10 : OGRGmtLayer::~OGRGmtLayer()
     245                 : 
     246                 : {
     247              10 :     if( m_nFeaturesRead > 0 && poFeatureDefn != NULL )
     248                 :     {
     249                 :         CPLDebug( "Gmt", "%d features read on layer '%s'.",
     250                 :                   (int) m_nFeaturesRead, 
     251               6 :                   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              10 :     if( nRegionOffset != 0 && bUpdate )
     259                 :     {
     260               4 :         VSIFSeekL( fp, nRegionOffset, SEEK_SET );
     261                 :         VSIFPrintfL( fp, "# @R%.12g/%.12g/%.12g/%.12g", 
     262                 :                      sRegion.MinX, 
     263                 :                      sRegion.MaxX,
     264                 :                      sRegion.MinY,
     265               4 :                      sRegion.MaxY );
     266                 :     }
     267                 :     
     268                 : /* -------------------------------------------------------------------- */
     269                 : /*      Clean up.                                                       */
     270                 : /* -------------------------------------------------------------------- */
     271              10 :     CSLDestroy( papszKeyedValues );
     272                 : 
     273              10 :     if( poFeatureDefn )
     274              10 :         poFeatureDefn->Release();
     275                 : 
     276              10 :     if( poSRS )
     277               2 :         poSRS->Release();
     278                 : 
     279              10 :     if( fp != NULL )
     280              10 :         VSIFCloseL( fp );
     281              10 : }
     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            1382 : int OGRGmtLayer::ReadLine()
     292                 : 
     293                 : {
     294                 : /* -------------------------------------------------------------------- */
     295                 : /*      Clear last line.                                                */
     296                 : /* -------------------------------------------------------------------- */
     297            1382 :     osLine.erase();
     298            1382 :     if( papszKeyedValues )
     299                 :     {
     300             228 :         CSLDestroy( papszKeyedValues );
     301             228 :         papszKeyedValues = NULL;
     302                 :     }
     303                 :     
     304                 : /* -------------------------------------------------------------------- */
     305                 : /*      Read newline.                                                   */
     306                 : /* -------------------------------------------------------------------- */
     307            1382 :     const char *pszLine = CPLReadLineL( fp );
     308            1382 :     if( pszLine == NULL )
     309              12 :         return FALSE; // end of file.
     310                 : 
     311            1370 :     osLine = pszLine;
     312                 : 
     313                 : /* -------------------------------------------------------------------- */
     314                 : /*      If this is a comment line with keyed values, parse them.        */
     315                 : /* -------------------------------------------------------------------- */
     316                 :     size_t i;
     317                 : 
     318            1610 :     if( osLine[0] != '#' || osLine.find_first_of('@') == std::string::npos )
     319            1142 :         return TRUE;
     320                 : 
     321            1234 :     for( i = 0; i < osLine.length(); i++ )
     322                 :     {
     323            1006 :         if( osLine[i] == '@' )
     324                 :         {
     325                 :             size_t iValEnd;
     326             230 :             int bInQuotes = FALSE;
     327                 : 
     328            3476 :             for( iValEnd = i+2; iValEnd < osLine.length(); iValEnd++ )
     329                 :             {
     330            3256 :                 if( !bInQuotes && isspace((unsigned char)osLine[iValEnd]) )
     331              10 :                     break;
     332                 : 
     333            3246 :                 if( bInQuotes && osLine[iValEnd] == '\\' 
     334                 :                     && iValEnd < osLine.length()-1 )
     335                 :                 {
     336              60 :                     iValEnd++;
     337                 :                 }
     338            3186 :                 else if( osLine[iValEnd] == '"' )
     339              32 :                     bInQuotes = !bInQuotes;
     340                 :             }
     341                 : 
     342             230 :             CPLString osValue = osLine.substr(i+2,iValEnd-i-2);
     343                 : 
     344                 :             // Unecape contents
     345                 :             char *pszUEValue = CPLUnescapeString( osValue, NULL, 
     346             230 :                                                   CPLES_BackslashQuotable );
     347                 :             
     348             230 :             CPLString osKeyValue = osLine.substr(i+1,1);
     349             230 :             osKeyValue += pszUEValue;
     350             230 :             CPLFree( pszUEValue );
     351             230 :             papszKeyedValues = CSLAddString( papszKeyedValues, osKeyValue );
     352                 : 
     353             230 :             i = iValEnd;
     354                 :         }
     355                 :     }
     356                 : 
     357             228 :     return TRUE;
     358                 : }
     359                 : 
     360                 : /************************************************************************/
     361                 : /*                            ResetReading()                            */
     362                 : /************************************************************************/
     363                 : 
     364               4 : void OGRGmtLayer::ResetReading()
     365                 : 
     366                 : {
     367               4 :     if( iNextFID != 0 )
     368                 :     {
     369               2 :         iNextFID = 0;
     370               2 :         VSIFSeekL( fp, 0, SEEK_SET );
     371               2 :         ReadLine();
     372                 :     }
     373               4 : }
     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              42 : int OGRGmtLayer::ScanAheadForHole()
     384                 : 
     385                 : {
     386              42 :     CPLString osSavedLine = osLine;
     387              42 :     vsi_l_offset nSavedLocation = VSIFTellL( fp );
     388                 : 
     389             162 :     while( ReadLine() && osLine[0] == '#' )
     390                 :     {
     391              80 :         if( papszKeyedValues != NULL && papszKeyedValues[0][0] == 'H' )
     392               2 :             return TRUE;
     393                 :     }
     394                 : 
     395              40 :     VSIFSeekL( fp, nSavedLocation, SEEK_SET );
     396              40 :     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              40 :     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              10 : int OGRGmtLayer::NextIsFeature()
     414                 : 
     415                 : {
     416              10 :     CPLString osSavedLine = osLine;
     417              10 :     vsi_l_offset nSavedLocation = VSIFTellL( fp );
     418              10 :     int bReturn = FALSE;
     419                 : 
     420              10 :     ReadLine();
     421                 : 
     422              10 :     if( osLine[0] == '#' && strstr(osLine,"@D") != NULL )
     423               4 :         bReturn = TRUE;
     424                 : 
     425              10 :     VSIFSeekL( fp, nSavedLocation, SEEK_SET );
     426              10 :     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              10 :     return bReturn;
     433                 : }
     434                 : 
     435                 : /************************************************************************/
     436                 : /*                         GetNextRawFeature()                          */
     437                 : /************************************************************************/
     438                 : 
     439              54 : OGRFeature *OGRGmtLayer::GetNextRawFeature()
     440                 : 
     441                 : {
     442                 :     int  bMultiVertex = 
     443                 :         poFeatureDefn->GetGeomType() != wkbPoint
     444              54 :         && poFeatureDefn->GetGeomType() != wkbUnknown;
     445              54 :     CPLString osFieldData;
     446              54 :     OGRGeometry *poGeom = NULL;
     447                 : 
     448                 : /* -------------------------------------------------------------------- */
     449                 : /*      Read lines associated with this feature.                        */
     450                 : /* -------------------------------------------------------------------- */
     451            1192 :     for( ; TRUE; ReadLine() )
     452                 :     {
     453            1246 :         if( osLine.length() == 0 )
     454              14 :             break;
     455                 : 
     456            1232 :         if( osLine[0] == '>' )
     457                 :         {
     458             144 :             if( poGeom != NULL 
     459              48 :                 && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon )
     460                 :             {
     461               6 :                 OGRMultiPolygon *poMP = (OGRMultiPolygon *) poGeom;
     462               6 :                 if( ScanAheadForHole() )
     463                 :                 {
     464                 :                     // Add a hole to the current polygon.
     465                 :                     ((OGRPolygon *) poMP->getGeometryRef(
     466                 :                         poMP->getNumGeometries()-1 ))->
     467               2 :                         addRingDirectly( new OGRLinearRing() );
     468                 :                 }
     469               4 :                 else if( !NextIsFeature() )
     470                 :                 {
     471               2 :                     OGRPolygon *poPoly = new OGRPolygon();
     472                 :                     
     473               4 :                     poPoly->addRingDirectly( new OGRLinearRing() );
     474                 : 
     475               2 :                     poMP->addGeometryDirectly( poPoly );
     476                 :                 }
     477                 :                 else
     478               2 :                     break; /* done geometry */
     479                 :             }
     480             132 :             else if( poGeom != NULL 
     481              42 :                      && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
     482                 :             {
     483              36 :                 if( ScanAheadForHole() )
     484                 :                     ((OGRPolygon *)poGeom)->
     485               0 :                         addRingDirectly( new OGRLinearRing() );
     486                 :                 else
     487              36 :                     break; /* done geometry */
     488                 :             }
     489              60 :             else if( poGeom != NULL 
     490               6 :                      && (wkbFlatten(poGeom->getGeometryType()) 
     491                 :                          == wkbMultiLineString)
     492                 :                      && !NextIsFeature() )
     493                 :             {
     494                 :                 ((OGRMultiLineString *) poGeom)->
     495               4 :                     addGeometryDirectly( new OGRLineString() );
     496                 :             }
     497              50 :             else if( poGeom != NULL )
     498                 :             {
     499               2 :                 break;
     500                 :             }
     501              48 :             else if( poFeatureDefn->GetGeomType() == wkbUnknown )
     502                 :             {
     503               0 :                 poFeatureDefn->SetGeomType( wkbLineString );
     504               0 :                 bMultiVertex = TRUE;
     505                 :             }
     506                 :         }
     507            1136 :         else if( osLine[0] == '#' )
     508                 :         {
     509                 :             int i;
     510             418 :             for( i = 0; 
     511             208 :                  papszKeyedValues != NULL && papszKeyedValues[i] != NULL; 
     512                 :                  i++ )
     513                 :             {
     514             104 :                 if( papszKeyedValues[i][0] == 'D' )
     515              48 :                     osFieldData = papszKeyedValues[i] + 1;
     516                 :             }
     517                 :         }
     518                 :         else
     519                 :         {
     520                 :             // Parse point line. 
     521            1030 :             double dfX, dfY, dfZ = 0.0;
     522            1030 :             int nDim = sscanf( osLine, "%lf %lf %lf", &dfX, &dfY, &dfZ );
     523                 :                 
     524            1030 :             if( nDim >= 2 )
     525                 :             {
     526            1030 :                 if( poGeom == NULL )
     527                 :                 {
     528              48 :                     switch( poFeatureDefn->GetGeomType() )
     529                 :                     {
     530                 :                       case wkbLineString:
     531               0 :                         poGeom = new OGRLineString();
     532               0 :                         break;
     533                 :                     
     534                 :                       case wkbPolygon:
     535              40 :                         poGeom = new OGRPolygon();
     536                 :                         ((OGRPolygon *) poGeom)->addRingDirectly(
     537              80 :                             new OGRLinearRing() );
     538              40 :                         break;
     539                 :                     
     540                 :                       case wkbMultiPolygon:
     541                 :                       {
     542               4 :                           OGRPolygon *poPoly = new OGRPolygon();
     543               8 :                           poPoly->addRingDirectly( new OGRLinearRing() );
     544                 : 
     545               4 :                           poGeom = new OGRMultiPolygon();
     546                 :                           ((OGRMultiPolygon *) poGeom)->
     547               4 :                               addGeometryDirectly( poPoly );
     548                 :                       }
     549               4 :                       break;
     550                 :                     
     551                 :                       case wkbMultiPoint:
     552               0 :                         poGeom = new OGRMultiPoint();
     553               0 :                         break;
     554                 : 
     555                 :                       case wkbMultiLineString:
     556               4 :                         poGeom = new OGRMultiLineString();
     557                 :                         ((OGRMultiLineString *) poGeom)->addGeometryDirectly(
     558               8 :                             new OGRLineString() );
     559               4 :                         break;
     560                 : 
     561                 :                       case wkbPoint:
     562                 :                       case wkbUnknown:
     563                 :                       default:
     564               0 :                         poGeom = new OGRPoint();
     565                 :                         break;
     566                 :                     }
     567                 : 
     568                 :                 }
     569                 : 
     570            1030 :                 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            1014 :                       if( wkbFlatten(poGeom->getGeometryType()) 
     593                 :                           == wkbMultiPolygon )
     594                 :                       {
     595              34 :                           OGRMultiPolygon *poMP = (OGRMultiPolygon *) poGeom;
     596                 :                           poPoly = (OGRPolygon*) poMP->getGeometryRef(
     597              34 :                               poMP->getNumGeometries() - 1 );
     598                 :                       }
     599                 :                       else
     600             980 :                           poPoly = (OGRPolygon *) poGeom;
     601                 : 
     602            1014 :                       if( poPoly->getNumInteriorRings() == 0 )
     603            1006 :                           poRing = poPoly->getExteriorRing();
     604                 :                       else
     605                 :                           poRing = poPoly->getInteriorRing(
     606               8 :                               poPoly->getNumInteriorRings()-1 );
     607                 :                       
     608            1014 :                       if( nDim == 3 )
     609               0 :                         poRing->addPoint(dfX,dfY,dfZ);
     610                 :                       else
     611            1014 :                         poRing->addPoint(dfX,dfY);
     612                 :                   }
     613            1014 :                   break;
     614                 : 
     615                 :                   case wkbMultiLineString:
     616                 :                   {
     617              16 :                       OGRMultiLineString *poML = (OGRMultiLineString *) poGeom;
     618                 :                       OGRLineString *poLine;
     619                 : 
     620                 :                       poLine = (OGRLineString *) 
     621              16 :                           poML->getGeometryRef( poML->getNumGeometries()-1 );
     622                 :                       
     623              16 :                       if( nDim == 3 )
     624               0 :                         poLine->addPoint(dfX,dfY,dfZ);
     625                 :                       else
     626              16 :                         poLine->addPoint(dfX,dfY);
     627                 :                   }
     628              16 :                   break;
     629                 : 
     630                 :                   default:
     631               0 :                     CPLAssert( FALSE );
     632                 :                 }
     633                 :             }
     634                 :         }
     635                 : 
     636            1192 :         if( poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint )
     637                 :         {
     638               0 :             ReadLine();
     639               0 :             break;
     640                 :         }
     641                 :     }
     642                 : 
     643              54 :     if( poGeom == NULL )
     644               6 :         return NULL;
     645                 : 
     646                 : /* -------------------------------------------------------------------- */
     647                 : /*      Create feature.                                                 */
     648                 : /* -------------------------------------------------------------------- */
     649              48 :     OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
     650              48 :     poFeature->SetGeometryDirectly( poGeom );
     651              48 :     poFeature->SetFID( iNextFID++ );
     652                 : 
     653                 : /* -------------------------------------------------------------------- */
     654                 : /*      Process field values.                                           */
     655                 : /* -------------------------------------------------------------------- */
     656              48 :     char **papszFD = CSLTokenizeStringComplex( osFieldData, "|", TRUE, TRUE );
     657                 :     int iField; 
     658                 : 
     659             184 :     for( iField = 0; papszFD != NULL && papszFD[iField] != NULL; iField++ )
     660                 :     {
     661             136 :         if( iField >= poFeatureDefn->GetFieldCount() )
     662               0 :             break;
     663                 : 
     664             136 :         poFeature->SetField( iField, papszFD[iField] );
     665                 :     }
     666                 : 
     667              48 :     CSLDestroy( papszFD );
     668                 : 
     669              48 :     m_nFeaturesRead++;
     670                 : 
     671              48 :     return poFeature;
     672                 : }
     673                 : 
     674                 : /************************************************************************/
     675                 : /*                           GetNextFeature()                           */
     676                 : /************************************************************************/
     677                 : 
     678              54 : OGRFeature *OGRGmtLayer::GetNextFeature()
     679                 : 
     680                 : {
     681              10 :     while( TRUE )
     682                 :     {
     683              54 :         OGRFeature *poFeature = GetNextRawFeature();
     684                 : 
     685              54 :         if( poFeature == NULL )
     686               6 :             return NULL;
     687                 : 
     688              48 :         if( (m_poFilterGeom == NULL
     689                 :              || FilterGeometry( poFeature->GetGeometryRef() ) )
     690                 :             && (m_poAttrQuery == NULL
     691                 :                 || m_poAttrQuery->Evaluate( poFeature ) ) )
     692                 :         {
     693              38 :             return poFeature;
     694                 :         }
     695                 :         else
     696                 :         {
     697              10 :             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               4 : 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               4 :     if( poFeatureDefn->GetGeomType() == wkbUnknown 
     719                 :         && poThisGeom != NULL )
     720                 :     {
     721                 :         const char *pszGeom;
     722                 : 
     723               4 :         poFeatureDefn->SetGeomType(wkbFlatten(poThisGeom->getGeometryType()));
     724                 : 
     725               4 :         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               2 :             pszGeom = " @GPOLYGON";
     735               2 :             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               2 :             pszGeom = " @GMULTIPOLYGON";
     744               2 :             break;
     745                 :           default:
     746               0 :             pszGeom = "";
     747                 :             break;
     748                 :         }
     749                 :         
     750               4 :         VSIFPrintfL( fp, "#%s\n", pszGeom );
     751                 :     }
     752                 : 
     753                 : /* -------------------------------------------------------------------- */
     754                 : /*      Prepare and write the field names and types.                    */
     755                 : /* -------------------------------------------------------------------- */
     756               4 :     CPLString osFieldNames, osFieldTypes;
     757                 :         
     758                 :     int iField;
     759                 : 
     760              12 :     for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
     761                 :     {
     762               8 :         if( iField > 0 )
     763                 :         {
     764               4 :             osFieldNames += "|";
     765               4 :             osFieldTypes += "|";
     766                 :         }
     767                 : 
     768               8 :         osFieldNames += poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
     769               8 :         switch( poFeatureDefn->GetFieldDefn(iField)->GetType() )
     770                 :         {
     771                 :           case OFTInteger:
     772               4 :             osFieldTypes += "integer";
     773               4 :             break;
     774                 :                 
     775                 :           case OFTReal:
     776               2 :             osFieldTypes += "double";
     777               2 :             break;
     778                 :                 
     779                 :           case OFTDateTime:
     780               0 :             osFieldTypes += "datetime";
     781               0 :             break;
     782                 :                 
     783                 :           default:
     784               2 :             osFieldTypes += "string";
     785                 :             break;
     786                 :         }
     787                 :     }
     788                 : 
     789               4 :     if( poFeatureDefn->GetFieldCount() > 0 )
     790                 :     {
     791               4 :         VSIFPrintfL( fp, "# @N%s\n", osFieldNames.c_str() );
     792               4 :         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               4 :     VSIFPrintfL( fp, "# FEATURE_DATA\n" );
     799                 : 
     800               4 :     bHeaderComplete = TRUE;
     801               4 :     bRegionComplete = TRUE; // no feature written, so we know them all!
     802                 : 
     803               4 :     return OGRERR_NONE;
     804                 : }
     805                 : 
     806                 : /************************************************************************/
     807                 : /*                           CreateFeature()                            */
     808                 : /************************************************************************/
     809                 : 
     810              24 : OGRErr OGRGmtLayer::CreateFeature( OGRFeature *poFeature )
     811                 : 
     812                 : {
     813              24 :     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              24 :     if( !bHeaderComplete )
     824                 :     {
     825               4 :         OGRErr eErr = CompleteHeader( poFeature->GetGeometryRef() );
     826                 : 
     827               4 :         if( eErr != OGRERR_NONE )
     828               0 :             return eErr;
     829                 :     }
     830                 : 
     831                 : /* -------------------------------------------------------------------- */
     832                 : /*      Write out the feature                                           */
     833                 : /* -------------------------------------------------------------------- */
     834              24 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     835                 :     
     836              24 :     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              24 :     if( poFeatureDefn->GetGeomType() == wkbUnknown )
     844               0 :         poFeatureDefn->SetGeomType(wkbFlatten(poGeom->getGeometryType()));
     845                 : 
     846                 :     // Do we need a vertex collection marker grouping vertices. 
     847              24 :     if( poFeatureDefn->GetGeomType() != wkbPoint )
     848              24 :         VSIFPrintfL( fp, ">\n" );
     849                 : 
     850                 : /* -------------------------------------------------------------------- */
     851                 : /*      Write feature properties()                                      */
     852                 : /* -------------------------------------------------------------------- */
     853              24 :     if( poFeatureDefn->GetFieldCount() > 0 )
     854                 :     {
     855                 :         int iField;
     856              24 :         CPLString osFieldData;
     857                 : 
     858              88 :         for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
     859                 :         {
     860              64 :             OGRFieldType eFType=poFeatureDefn->GetFieldDefn(iField)->GetType();
     861              64 :             const char *pszRawValue = poFeature->GetFieldAsString(iField);
     862                 :             char *pszEscapedVal;
     863                 : 
     864              64 :             if( iField > 0 )
     865              40 :                 osFieldData += "|";
     866                 : 
     867                 :             // We don't want prefix spaces for numeric values.
     868              64 :             if( eFType == OFTInteger || eFType == OFTReal )
     869              88 :                 while( *pszRawValue == ' ' )
     870               0 :                     pszRawValue++;
     871                 : 
     872              64 :             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              64 :                 osFieldData += pszRawValue;
     886                 :         }
     887                 : 
     888              24 :         VSIFPrintfL( fp, "# @D%s\n", osFieldData.c_str() );
     889                 :     }
     890                 : 
     891                 : /* -------------------------------------------------------------------- */
     892                 : /*      Write Geometry                                                  */
     893                 : /* -------------------------------------------------------------------- */
     894              24 :     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              58 : OGRErr OGRGmtLayer::WriteGeometry( OGRGeometryH hGeom, int bHaveAngle )
     909                 : 
     910                 : {
     911                 : /* -------------------------------------------------------------------- */
     912                 : /*      This is a geometry with sub-geometries.                         */
     913                 : /* -------------------------------------------------------------------- */
     914              58 :     if( OGR_G_GetGeometryCount( hGeom ) > 0 )
     915                 :     {
     916                 :         int iGeom;
     917              30 :         OGRErr eErr = OGRERR_NONE;
     918                 :         
     919              64 :         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              34 :             if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon )
     927                 :             {
     928              28 :                 if( !bHaveAngle )
     929                 :                 {
     930               4 :                     VSIFPrintfL( fp, ">\n" );
     931               4 :                     bHaveAngle = TRUE;
     932                 :                 }
     933              28 :                 if( iGeom == 0 )
     934              26 :                     VSIFPrintfL( fp, "# @P\n" );
     935                 :                 else
     936               2 :                     VSIFPrintfL( fp, "# @H\n" );
     937                 :             }
     938                 : 
     939                 :             eErr = WriteGeometry( OGR_G_GetGeometryRef( hGeom, iGeom ), 
     940              34 :                                   bHaveAngle );
     941              34 :             bHaveAngle = FALSE;
     942                 :         }
     943              30 :         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              28 :     if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) != wkbPoint 
     951                 :         && !bHaveAngle )
     952               0 :         VSIFPrintfL( fp, ">\n" );
     953                 : 
     954                 : /* -------------------------------------------------------------------- */
     955                 : /*      Dump vertices.                                                  */
     956                 : /* -------------------------------------------------------------------- */
     957              28 :     int iPoint, nPointCount = OGR_G_GetPointCount(hGeom);
     958              28 :     int nDim = OGR_G_GetCoordinateDimension(hGeom);
     959                 : 
     960             552 :     for( iPoint = 0; iPoint < nPointCount; iPoint++ )
     961                 :     {
     962                 :         char   szLine[128];
     963             524 :         double dfX = OGR_G_GetX( hGeom, iPoint );
     964             524 :         double dfY = OGR_G_GetY( hGeom, iPoint );
     965             524 :         double dfZ = OGR_G_GetZ( hGeom, iPoint );
     966                 : 
     967             524 :         sRegion.Merge( dfX, dfY );
     968             524 :         OGRMakeWktCoordinate( szLine, dfX, dfY, dfZ, nDim );
     969             524 :         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              28 :     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               8 : OGRErr OGRGmtLayer::CreateField( OGRFieldDefn *poField, int bApproxOK )
    1034                 : 
    1035                 : {
    1036               8 :     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               8 :     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               8 :     switch( poField->GetType() )
    1051                 :     {
    1052                 :       case OFTInteger:
    1053                 :       case OFTReal:
    1054                 :       case OFTString:
    1055                 :       case OFTDateTime:
    1056               8 :         poFeatureDefn->AddFieldDefn( poField );
    1057               8 :         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