LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmt - ogrgmtlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 426 496 85.9 %
Date: 2026-09-18 08:43:21 Functions: 13 14 92.9 %

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

Generated by: LCOV version 1.14