LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmt - ogrgmtlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 416 489 85.1 %
Date: 2026-05-29 23:25:07 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         107 : OGRGmtLayer::OGRGmtLayer(GDALDataset *poDS, const char *pszFilename,
      24             :                          VSILFILE *fp, const OGRSpatialReference *poSRS,
      25         107 :                          bool bUpdateIn)
      26             :     : m_poDS(poDS), poFeatureDefn(nullptr), iNextFID(0), bUpdate(bUpdateIn),
      27             :       // Assume header complete in readonly mode.
      28         107 :       bHeaderComplete(!bUpdate), bRegionComplete(false), nRegionOffset(0),
      29         107 :       m_fp(fp ? fp : VSIFOpenL(pszFilename, (bUpdateIn ? "r+" : "r"))),
      30         214 :       papszKeyedValues(nullptr), bValidFile(false)
      31             : {
      32         107 :     if (m_fp == nullptr)
      33          35 :         return;
      34             : 
      35             :     /* -------------------------------------------------------------------- */
      36             :     /*      Create the feature definition                                   */
      37             :     /* -------------------------------------------------------------------- */
      38          72 :     poFeatureDefn = new OGRFeatureDefn(CPLGetBasenameSafe(pszFilename).c_str());
      39          72 :     SetDescription(poFeatureDefn->GetName());
      40          72 :     poFeatureDefn->Reference();
      41             : 
      42             :     /* -------------------------------------------------------------------- */
      43             :     /*      Read the header.                                                */
      44             :     /* -------------------------------------------------------------------- */
      45          72 :     if (!STARTS_WITH(pszFilename, "/vsistdout"))
      46             :     {
      47         142 :         CPLString osFieldNames;
      48         142 :         CPLString osFieldTypes;
      49         142 :         CPLString osGeometryType;
      50         142 :         CPLString osRegion;
      51         142 :         CPLString osWKT;
      52         142 :         CPLString osProj4;
      53         142 :         CPLString osEPSG;
      54          71 :         vsi_l_offset nStartOfLine = 0;
      55             : 
      56          71 :         VSIFSeekL(m_fp, 0, SEEK_SET);
      57             : 
      58         258 :         while (ReadLine() && osLine[0] == '#')
      59             :         {
      60         207 :             if (strstr(osLine, "FEATURE_DATA"))
      61             :             {
      62          20 :                 bHeaderComplete = true;
      63          20 :                 ReadLine();
      64          20 :                 break;
      65             :             }
      66             : 
      67         187 :             if (STARTS_WITH_CI(osLine, "# REGION_STUB "))
      68          34 :                 nRegionOffset = nStartOfLine;
      69             : 
      70         390 :             for (int iKey = 0; papszKeyedValues != nullptr &&
      71         356 :                                papszKeyedValues[iKey] != nullptr;
      72             :                  iKey++)
      73             :             {
      74         203 :                 if (papszKeyedValues[iKey][0] == 'N')
      75          20 :                     osFieldNames = papszKeyedValues[iKey] + 1;
      76         203 :                 if (papszKeyedValues[iKey][0] == 'T')
      77          20 :                     osFieldTypes = papszKeyedValues[iKey] + 1;
      78         203 :                 if (papszKeyedValues[iKey][0] == 'G')
      79          52 :                     osGeometryType = papszKeyedValues[iKey] + 1;
      80         203 :                 if (papszKeyedValues[iKey][0] == 'R')
      81          35 :                     osRegion = papszKeyedValues[iKey] + 1;
      82         203 :                 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         187 :             nStartOfLine = VSIFTellL(m_fp);
     107             :         }
     108             : 
     109             :         /* --------------------------------------------------------------------
     110             :          */
     111             :         /*      Handle coordinate system. */
     112             :         /* --------------------------------------------------------------------
     113             :          */
     114          71 :         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          69 :         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          69 :         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          71 :         if (osGeometryType == "POINT")
     146           8 :             poFeatureDefn->SetGeomType(wkbPoint);
     147          63 :         else if (osGeometryType == "MULTIPOINT")
     148           8 :             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          10 :             poFeatureDefn->SetGeomType(wkbPolygon);
     155          28 :         else if (osGeometryType == "MULTIPOLYGON")
     156           9 :             poFeatureDefn->SetGeomType(wkbMultiPolygon);
     157             : 
     158             :         /* --------------------------------------------------------------------
     159             :          */
     160             :         /*      Process a region line. */
     161             :         /* --------------------------------------------------------------------
     162             :          */
     163          71 :         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          71 :         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          72 :     poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
     232             : 
     233          72 :     bValidFile = true;
     234             : }
     235             : 
     236             : /************************************************************************/
     237             : /*                            ~OGRGmtLayer()                            */
     238             : /************************************************************************/
     239             : 
     240         214 : OGRGmtLayer::~OGRGmtLayer()
     241             : 
     242             : {
     243         107 :     if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
     244             :     {
     245          10 :         CPLDebug("Gmt", "%d features read on layer '%s'.",
     246           5 :                  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         107 :     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         107 :     CSLDestroy(papszKeyedValues);
     264             : 
     265         107 :     if (poFeatureDefn)
     266          72 :         poFeatureDefn->Release();
     267             : 
     268         107 :     if (m_poSRS)
     269           3 :         m_poSRS->Release();
     270             : 
     271         107 :     if (m_fp != nullptr)
     272          72 :         VSIFCloseL(m_fp);
     273         214 : }
     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        1244 : bool OGRGmtLayer::ReadLine()
     284             : 
     285             : {
     286             :     /* -------------------------------------------------------------------- */
     287             :     /*      Clear last line.                                                */
     288             :     /* -------------------------------------------------------------------- */
     289        1244 :     osLine.erase();
     290        1244 :     if (papszKeyedValues)
     291             :     {
     292         286 :         CSLDestroy(papszKeyedValues);
     293         286 :         papszKeyedValues = nullptr;
     294             :     }
     295             : 
     296             :     /* -------------------------------------------------------------------- */
     297             :     /*      Read newline.                                                   */
     298             :     /* -------------------------------------------------------------------- */
     299        1244 :     const char *pszLine = CPLReadLineL(m_fp);
     300        1244 :     if (pszLine == nullptr)
     301          56 :         return false;  // end of file.
     302             : 
     303        1188 :     osLine = pszLine;
     304             : 
     305             :     /* -------------------------------------------------------------------- */
     306             :     /*      If this is a comment line with keyed values, parse them.        */
     307             :     /* -------------------------------------------------------------------- */
     308             : 
     309        1188 :     if (osLine[0] != '#' || osLine.find_first_of('@') == std::string::npos)
     310         898 :         return true;
     311             : 
     312         290 :     CPLStringList aosKeyedValues;
     313        3163 :     for (size_t i = 0; i < osLine.length(); i++)
     314             :     {
     315        2873 :         if (osLine[i] == '@' && i + 2 <= osLine.size())
     316             :         {
     317         341 :             bool bInQuotes = false;
     318             : 
     319         341 :             size_t iValEnd = i + 2;  // Used after for.
     320        5660 :             for (; iValEnd < osLine.length(); iValEnd++)
     321             :             {
     322        9610 :                 if (!bInQuotes &&
     323        4204 :                     isspace(static_cast<unsigned char>(osLine[iValEnd])))
     324          87 :                     break;
     325             : 
     326        6511 :                 if (bInQuotes && iValEnd < osLine.length() - 1 &&
     327        1192 :                     osLine[iValEnd] == '\\')
     328             :                 {
     329          88 :                     iValEnd++;
     330             :                 }
     331        5231 :                 else if (osLine[iValEnd] == '"')
     332          26 :                     bInQuotes = !bInQuotes;
     333             :             }
     334             : 
     335         682 :             const CPLString osValue = osLine.substr(i + 2, iValEnd - i - 2);
     336             : 
     337             :             // Unecape contents
     338             :             char *pszUEValue =
     339         341 :                 CPLUnescapeString(osValue, nullptr, CPLES_BackslashQuotable);
     340             : 
     341         341 :             CPLString osKeyValue = osLine.substr(i + 1, 1);
     342         341 :             osKeyValue += pszUEValue;
     343         341 :             CPLFree(pszUEValue);
     344         341 :             aosKeyedValues.AddString(osKeyValue);
     345             : 
     346         341 :             i = iValEnd;
     347             :         }
     348             :     }
     349         290 :     papszKeyedValues = aosKeyedValues.StealList();
     350             : 
     351         290 :     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           5 : bool OGRGmtLayer::NextIsFeature()
     408             : 
     409             : {
     410           5 :     CPLString osSavedLine = osLine;
     411           5 :     const vsi_l_offset nSavedLocation = VSIFTellL(m_fp);
     412           5 :     bool bReturn = false;
     413             : 
     414           5 :     ReadLine();
     415             : 
     416           5 :     if (osLine[0] == '#' && strstr(osLine, "@D") != nullptr)
     417           2 :         bReturn = true;
     418             : 
     419           5 :     VSIFSeekL(m_fp, nSavedLocation, SEEK_SET);
     420           5 :     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          10 :     return bReturn;
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                         GetNextRawFeature()                          */
     431             : /************************************************************************/
     432             : 
     433          55 : OGRFeature *OGRGmtLayer::GetNextRawFeature()
     434             : 
     435             : {
     436             : #if 0
     437             :     bool bMultiVertex =
     438             :         poFeatureDefn->GetGeomType() != wkbPoint
     439             :         && poFeatureDefn->GetGeomType() != wkbUnknown;
     440             : #endif
     441         110 :     CPLString osFieldData;
     442          55 :     OGRGeometry *poGeom = nullptr;
     443             : 
     444             :     /* -------------------------------------------------------------------- */
     445             :     /*      Read lines associated with this feature.                        */
     446             :     /* -------------------------------------------------------------------- */
     447         871 :     for (; true; ReadLine())
     448             :     {
     449         926 :         if (osLine.length() == 0)
     450          25 :             break;
     451             : 
     452         901 :         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           3 :                 OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
     460           3 :                 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           2 :                 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           1 :                     break; /* done geometry */
     476             :             }
     477          64 :             else if (eType == wkbPolygon)
     478             :             {
     479          27 :                 if (ScanAheadForHole())
     480           0 :                     poGeom->toPolygon()->addRingDirectly(new OGRLinearRing());
     481             :                 else
     482          27 :                     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         834 :         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         761 :             double dfX = 0.0;
     513         761 :             double dfY = 0.0;
     514         761 :             double dfZ = 0.0;
     515         761 :             const int nDim = CPLsscanf(osLine, "%lf %lf %lf", &dfX, &dfY, &dfZ);
     516             : 
     517         761 :             if (nDim >= 2)
     518             :             {
     519         761 :                 if (poGeom == nullptr)
     520             :                 {
     521          35 :                     switch (poFeatureDefn->GetGeomType())
     522             :                     {
     523           0 :                         case wkbLineString:
     524           0 :                             poGeom = new OGRLineString();
     525           0 :                             break;
     526             : 
     527          30 :                         case wkbPolygon:
     528             :                         {
     529          30 :                             OGRPolygon *poPoly = new OGRPolygon();
     530          30 :                             poGeom = poPoly;
     531          30 :                             poPoly->addRingDirectly(new OGRLinearRing());
     532          30 :                             break;
     533             :                         }
     534             : 
     535           2 :                         case wkbMultiPolygon:
     536             :                         {
     537           2 :                             OGRPolygon *poPoly = new OGRPolygon();
     538           2 :                             poPoly->addRingDirectly(new OGRLinearRing());
     539             : 
     540           2 :                             OGRMultiPolygon *poMP = new OGRMultiPolygon();
     541           2 :                             poGeom = poMP;
     542           2 :                             poMP->addGeometryDirectly(poPoly);
     543             :                         }
     544           2 :                         break;
     545             : 
     546           0 :                         case wkbMultiPoint:
     547           0 :                             poGeom = new OGRMultiPoint();
     548           0 :                             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         761 :                 CPLAssert(poGeom != nullptr);
     568             :                 // cppcheck-suppress nullPointerRedundantCheck
     569         761 :                 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          17 :                             OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
     600          17 :                             poPoly = poMP->getGeometryRef(
     601          17 :                                 poMP->getNumGeometries() - 1);
     602             :                         }
     603             :                         else
     604         735 :                             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           0 :                     default:
     634           0 :                         CPLAssert(false);
     635             :                 }
     636             :             }
     637             :         }
     638             : 
     639         872 :         if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     640             :         {
     641           1 :             ReadLine();
     642           1 :             break;
     643             :         }
     644         871 :     }
     645             : 
     646          55 :     if (poGeom == nullptr)
     647          20 :         return nullptr;
     648             : 
     649             :     /* -------------------------------------------------------------------- */
     650             :     /*      Create feature.                                                 */
     651             :     /* -------------------------------------------------------------------- */
     652          35 :     OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
     653          35 :     poGeom->assignSpatialReference(m_poSRS);
     654          35 :     poFeature->SetGeometryDirectly(poGeom);
     655          35 :     poFeature->SetFID(iNextFID++);
     656             : 
     657             :     /* -------------------------------------------------------------------- */
     658             :     /*      Process field values.                                           */
     659             :     /* -------------------------------------------------------------------- */
     660          35 :     char **papszFD = CSLTokenizeStringComplex(osFieldData, "|", TRUE, TRUE);
     661             : 
     662         133 :     for (int iField = 0; papszFD != nullptr && papszFD[iField] != nullptr;
     663             :          iField++)
     664             :     {
     665          98 :         if (iField >= poFeatureDefn->GetFieldCount())
     666           0 :             break;
     667             : 
     668          98 :         poFeature->SetField(iField, papszFD[iField]);
     669             :     }
     670             : 
     671          35 :     CSLDestroy(papszFD);
     672             : 
     673          35 :     m_nFeaturesRead++;
     674             : 
     675          35 :     return poFeature;
     676             : }
     677             : 
     678             : /************************************************************************/
     679             : /*                           CompleteHeader()                           */
     680             : /*                                                                      */
     681             : /*      Finish writing out the header with field definitions and the    */
     682             : /*      layer geometry type.                                            */
     683             : /************************************************************************/
     684             : 
     685          19 : OGRErr OGRGmtLayer::CompleteHeader(OGRGeometry *poThisGeom)
     686             : 
     687             : {
     688             :     /* -------------------------------------------------------------------- */
     689             :     /*      If we do not already have a geometry type, try to work one      */
     690             :     /*      out and write it now.                                           */
     691             :     /* -------------------------------------------------------------------- */
     692          19 :     if (poFeatureDefn->GetGeomType() == wkbUnknown && poThisGeom != nullptr)
     693             :     {
     694           2 :         poFeatureDefn->SetGeomType(wkbFlatten(poThisGeom->getGeometryType()));
     695             : 
     696           2 :         const char *pszGeom = nullptr;
     697           2 :         switch (wkbFlatten(poFeatureDefn->GetGeomType()))
     698             :         {
     699           0 :             case wkbPoint:
     700           0 :                 pszGeom = " @GPOINT";
     701           0 :                 break;
     702           0 :             case wkbLineString:
     703           0 :                 pszGeom = " @GLINESTRING";
     704           0 :                 break;
     705           1 :             case wkbPolygon:
     706           1 :                 pszGeom = " @GPOLYGON";
     707           1 :                 break;
     708           0 :             case wkbMultiPoint:
     709           0 :                 pszGeom = " @GMULTIPOINT";
     710           0 :                 break;
     711           0 :             case wkbMultiLineString:
     712           0 :                 pszGeom = " @GMULTILINESTRING";
     713           0 :                 break;
     714           1 :             case wkbMultiPolygon:
     715           1 :                 pszGeom = " @GMULTIPOLYGON";
     716           1 :                 break;
     717           0 :             default:
     718           0 :                 pszGeom = "";
     719           0 :                 break;
     720             :         }
     721             : 
     722           2 :         VSIFPrintfL(m_fp, "#%s\n", pszGeom);
     723             :     }
     724             : 
     725             :     /* -------------------------------------------------------------------- */
     726             :     /*      Prepare and write the field names and types.                    */
     727             :     /* -------------------------------------------------------------------- */
     728          38 :     CPLString osFieldNames;
     729          19 :     CPLString osFieldTypes;
     730             : 
     731         106 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
     732             :     {
     733          87 :         if (iField > 0)
     734             :         {
     735          68 :             osFieldNames += "|";
     736          68 :             osFieldTypes += "|";
     737             :         }
     738             : 
     739          87 :         osFieldNames += poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
     740          87 :         switch (poFeatureDefn->GetFieldDefn(iField)->GetType())
     741             :         {
     742          18 :             case OFTInteger:
     743          18 :                 osFieldTypes += "integer";
     744          18 :                 break;
     745             : 
     746          19 :             case OFTReal:
     747          19 :                 osFieldTypes += "double";
     748          19 :                 break;
     749             : 
     750          16 :             case OFTDateTime:
     751          16 :                 osFieldTypes += "datetime";
     752          16 :                 break;
     753             : 
     754          34 :             default:
     755          34 :                 osFieldTypes += "string";
     756          34 :                 break;
     757             :         }
     758             :     }
     759             : 
     760          19 :     if (poFeatureDefn->GetFieldCount() > 0)
     761             :     {
     762          19 :         VSIFPrintfL(m_fp, "# @N%s\n", osFieldNames.c_str());
     763          19 :         VSIFPrintfL(m_fp, "# @T%s\n", osFieldTypes.c_str());
     764             :     }
     765             : 
     766             :     /* -------------------------------------------------------------------- */
     767             :     /*      Mark the end of the header, and start of feature data.          */
     768             :     /* -------------------------------------------------------------------- */
     769          19 :     VSIFPrintfL(m_fp, "# FEATURE_DATA\n");
     770             : 
     771          19 :     bHeaderComplete = true;
     772          19 :     bRegionComplete = true;  // no feature written, so we know them all!
     773             : 
     774          38 :     return OGRERR_NONE;
     775             : }
     776             : 
     777             : /************************************************************************/
     778             : /*                           ICreateFeature()                           */
     779             : /************************************************************************/
     780             : 
     781          86 : OGRErr OGRGmtLayer::ICreateFeature(OGRFeature *poFeature)
     782             : 
     783             : {
     784          86 :     if (!bUpdate)
     785             :     {
     786           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     787             :                  "Cannot create features on read-only dataset.");
     788           0 :         return OGRERR_FAILURE;
     789             :     }
     790             : 
     791             :     /* -------------------------------------------------------------------- */
     792             :     /*      Do we need to write the header describing the fields?           */
     793             :     /* -------------------------------------------------------------------- */
     794          86 :     if (!bHeaderComplete)
     795             :     {
     796          19 :         OGRErr eErr = CompleteHeader(poFeature->GetGeometryRef());
     797             : 
     798          19 :         if (eErr != OGRERR_NONE)
     799           0 :             return eErr;
     800             :     }
     801             : 
     802             :     /* -------------------------------------------------------------------- */
     803             :     /*      Write out the feature                                           */
     804             :     /* -------------------------------------------------------------------- */
     805          86 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     806             : 
     807          86 :     if (poGeom == nullptr)
     808             :     {
     809          33 :         CPLError(CE_Failure, CPLE_AppDefined,
     810             :                  "Features without geometry not supported by GMT writer.");
     811          33 :         return OGRERR_FAILURE;
     812             :     }
     813             : 
     814          53 :     if (poFeatureDefn->GetGeomType() == wkbUnknown)
     815           4 :         poFeatureDefn->SetGeomType(wkbFlatten(poGeom->getGeometryType()));
     816             : 
     817             :     // Do we need a vertex collection marker grouping vertices.
     818          53 :     if (poFeatureDefn->GetGeomType() != wkbPoint)
     819          47 :         VSIFPrintfL(m_fp, ">\n");
     820             : 
     821             :     /* -------------------------------------------------------------------- */
     822             :     /*      Write feature properties()                                      */
     823             :     /* -------------------------------------------------------------------- */
     824          53 :     if (poFeatureDefn->GetFieldCount() > 0)
     825             :     {
     826         106 :         CPLString osFieldData;
     827             : 
     828         270 :         for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
     829             :         {
     830             :             OGRFieldType eFType =
     831         217 :                 poFeatureDefn->GetFieldDefn(iField)->GetType();
     832         217 :             const char *pszRawValue = poFeature->GetFieldAsString(iField);
     833             : 
     834         217 :             if (iField > 0)
     835         164 :                 osFieldData += "|";
     836             : 
     837             :             // We do not want prefix spaces for numeric values.
     838         217 :             if (eFType == OFTInteger || eFType == OFTReal)
     839         104 :                 while (*pszRawValue == ' ')
     840           0 :                     pszRawValue++;
     841             : 
     842         217 :             if (strchr(pszRawValue, ' ') || strchr(pszRawValue, '|') ||
     843         186 :                 strchr(pszRawValue, '\t') || strchr(pszRawValue, '\n'))
     844             :             {
     845          31 :                 osFieldData += "\"";
     846             : 
     847             :                 char *pszEscapedVal =
     848          31 :                     CPLEscapeString(pszRawValue, -1, CPLES_BackslashQuotable);
     849          31 :                 osFieldData += pszEscapedVal;
     850          31 :                 CPLFree(pszEscapedVal);
     851             : 
     852          31 :                 osFieldData += "\"";
     853             :             }
     854             :             else
     855         186 :                 osFieldData += pszRawValue;
     856             :         }
     857             : 
     858          53 :         VSIFPrintfL(m_fp, "# @D%s\n", osFieldData.c_str());
     859             :     }
     860             : 
     861             :     /* -------------------------------------------------------------------- */
     862             :     /*      Write Geometry                                                  */
     863             :     /* -------------------------------------------------------------------- */
     864          53 :     return WriteGeometry(OGRGeometry::ToHandle(poGeom), true);
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                           WriteGeometry()                            */
     869             : /*                                                                      */
     870             : /*      Write a geometry to the file.  If bHaveAngle is true it         */
     871             : /*      means the angle bracket preceding the point stream has          */
     872             : /*      already been written out.                                       */
     873             : /*                                                                      */
     874             : /*      We use the C API for geometry access because of its            */
     875             : /*      simplified access to vertices and children geometries.          */
     876             : /************************************************************************/
     877             : 
     878         116 : OGRErr OGRGmtLayer::WriteGeometry(OGRGeometryH hGeom, bool bHaveAngle)
     879             : 
     880             : {
     881             :     /* -------------------------------------------------------------------- */
     882             :     /*      This is a geometry with sub-geometries.                         */
     883             :     /* -------------------------------------------------------------------- */
     884         116 :     if (OGR_G_GetGeometryCount(hGeom) > 0)
     885             :     {
     886          53 :         OGRErr eErr = OGRERR_NONE;
     887             : 
     888          53 :         for (int iGeom = 0;
     889         116 :              iGeom < OGR_G_GetGeometryCount(hGeom) && eErr == OGRERR_NONE;
     890             :              iGeom++)
     891             :         {
     892             :             // We need to emit polygon @P and @H items while we still
     893             :             // know this is a polygon and which is the outer and inner
     894             :             // ring.
     895          63 :             if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon)
     896             :             {
     897          36 :                 if (!bHaveAngle)
     898             :                 {
     899           6 :                     VSIFPrintfL(m_fp, ">\n");
     900           6 :                     bHaveAngle = true;
     901             :                 }
     902          36 :                 if (iGeom == 0)
     903          35 :                     VSIFPrintfL(m_fp, "# @P\n");
     904             :                 else
     905           1 :                     VSIFPrintfL(m_fp, "# @H\n");
     906             :             }
     907             : 
     908             :             eErr =
     909          63 :                 WriteGeometry(OGR_G_GetGeometryRef(hGeom, iGeom), bHaveAngle);
     910          63 :             bHaveAngle = false;
     911             :         }
     912          53 :         return eErr;
     913             :     }
     914             : 
     915             :     /* -------------------------------------------------------------------- */
     916             :     /*      If this is not a point we need to have an angle bracket to      */
     917             :     /*      mark the vertex list.                                           */
     918             :     /* -------------------------------------------------------------------- */
     919          63 :     if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) != wkbPoint && !bHaveAngle)
     920           4 :         VSIFPrintfL(m_fp, ">\n");
     921             : 
     922             :     /* -------------------------------------------------------------------- */
     923             :     /*      Dump vertices.                                                  */
     924             :     /* -------------------------------------------------------------------- */
     925          63 :     const int nPointCount = OGR_G_GetPointCount(hGeom);
     926          63 :     const int nDim = OGR_G_GetCoordinateDimension(hGeom);
     927             :     // For testing only. Ticket #6453
     928             :     const bool bUseTab =
     929          63 :         CPLTestBool(CPLGetConfigOption("GMT_USE_TAB", "FALSE"));
     930             : 
     931         671 :     for (int iPoint = 0; iPoint < nPointCount; iPoint++)
     932             :     {
     933         608 :         const double dfX = OGR_G_GetX(hGeom, iPoint);
     934         608 :         const double dfY = OGR_G_GetY(hGeom, iPoint);
     935         608 :         const double dfZ = OGR_G_GetZ(hGeom, iPoint);
     936             : 
     937         608 :         sRegion.Merge(dfX, dfY);
     938             :         char szLine[128];
     939         608 :         OGRMakeWktCoordinate(szLine, dfX, dfY, dfZ, nDim);
     940         608 :         if (bUseTab)
     941             :         {
     942          60 :             for (char *szPtr = szLine; *szPtr != '\0'; ++szPtr)
     943             :             {
     944          47 :                 if (*szPtr == ' ')
     945          13 :                     *szPtr = '\t';
     946             :             }
     947             :         }
     948         608 :         if (VSIFPrintfL(m_fp, "%s\n", szLine) < 1)
     949             :         {
     950           0 :             CPLError(CE_Failure, CPLE_FileIO, "Gmt write failure: %s",
     951           0 :                      VSIStrerror(errno));
     952           0 :             return OGRERR_FAILURE;
     953             :         }
     954             :     }
     955             : 
     956          63 :     return OGRERR_NONE;
     957             : }
     958             : 
     959             : /************************************************************************/
     960             : /*                            IGetExtent()                              */
     961             : /*                                                                      */
     962             : /*      Fetch extent of the data currently stored in the dataset.       */
     963             : /*      The bForce flag has no effect on SHO files since that value     */
     964             : /*      is always in the header.                                        */
     965             : /*                                                                      */
     966             : /*      Returns OGRERR_NONE/OGRRERR_FAILURE.                            */
     967             : /************************************************************************/
     968             : 
     969           0 : OGRErr OGRGmtLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
     970             :                                bool bForce)
     971             : 
     972             : {
     973           0 :     if (bRegionComplete && sRegion.IsInit())
     974             :     {
     975           0 :         *psExtent = sRegion;
     976           0 :         return OGRERR_NONE;
     977             :     }
     978             : 
     979           0 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
     980             : }
     981             : 
     982             : /************************************************************************/
     983             : /*                           TestCapability()                           */
     984             : /************************************************************************/
     985             : 
     986          74 : int OGRGmtLayer::TestCapability(const char *pszCap) const
     987             : 
     988             : {
     989          74 :     if (EQUAL(pszCap, OLCRandomRead))
     990           0 :         return FALSE;
     991             : 
     992          74 :     if (EQUAL(pszCap, OLCSequentialWrite))
     993          16 :         return TRUE;
     994             : 
     995          58 :     if (EQUAL(pszCap, OLCFastSpatialFilter))
     996           0 :         return FALSE;
     997             : 
     998          58 :     if (EQUAL(pszCap, OLCFastGetExtent))
     999           0 :         return bRegionComplete;
    1000             : 
    1001          58 :     if (EQUAL(pszCap, OLCCreateField))
    1002          16 :         return TRUE;
    1003             : 
    1004          42 :     if (EQUAL(pszCap, OLCZGeometries))
    1005           1 :         return TRUE;
    1006             : 
    1007          41 :     return FALSE;
    1008             : }
    1009             : 
    1010             : /************************************************************************/
    1011             : /*                            CreateField()                             */
    1012             : /************************************************************************/
    1013             : 
    1014          87 : OGRErr OGRGmtLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
    1015             : 
    1016             : {
    1017          87 :     if (!bUpdate)
    1018             :     {
    1019           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1020             :                  "Cannot create fields on read-only dataset.");
    1021           0 :         return OGRERR_FAILURE;
    1022             :     }
    1023             : 
    1024          87 :     if (bHeaderComplete)
    1025             :     {
    1026           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1027             :                  "Unable to create fields after features have been created.");
    1028           0 :         return OGRERR_FAILURE;
    1029             :     }
    1030             : 
    1031          87 :     switch (poField->GetType())
    1032             :     {
    1033          71 :         case OFTInteger:
    1034             :         case OFTReal:
    1035             :         case OFTString:
    1036             :         case OFTDateTime:
    1037          71 :             poFeatureDefn->AddFieldDefn(poField);
    1038          71 :             return OGRERR_NONE;
    1039             : 
    1040          16 :         default:
    1041          16 :             if (!bApproxOK)
    1042             :             {
    1043           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1044             :                          "Field %s is of unsupported type %s.",
    1045             :                          poField->GetNameRef(),
    1046             :                          poField->GetFieldTypeName(poField->GetType()));
    1047           0 :                 return OGRERR_FAILURE;
    1048             :             }
    1049          16 :             else if (poField->GetType() == OFTDate ||
    1050           0 :                      poField->GetType() == OFTTime)
    1051             :             {
    1052          16 :                 OGRFieldDefn oModDef(poField);
    1053          16 :                 oModDef.SetType(OFTDateTime);
    1054          16 :                 poFeatureDefn->AddFieldDefn(poField);
    1055          16 :                 return OGRERR_NONE;
    1056             :             }
    1057             :             else
    1058             :             {
    1059           0 :                 OGRFieldDefn oModDef(poField);
    1060           0 :                 oModDef.SetType(OFTString);
    1061           0 :                 poFeatureDefn->AddFieldDefn(poField);
    1062           0 :                 return OGRERR_NONE;
    1063             :             }
    1064             :     }
    1065             : }

Generated by: LCOV version 1.14